blob: d806309cd34f032bea1584d47a091e4ceda742f5 [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;
Mike Lockwood2f6a3882011-05-09 19:08:06 -070020import com.android.internal.util.XmlUtils;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080021import com.android.server.am.ActivityManagerService;
Kenny Rootcf0b38c2011-03-22 14:17:59 -070022import com.android.server.pm.PackageManagerService;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080023
Jason parks8888c592011-01-20 22:46:41 -060024import android.Manifest;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.content.BroadcastReceiver;
Kenny Roota02b8b02010-08-05 16:14:17 -070026import android.content.ComponentName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.content.Context;
28import android.content.Intent;
29import android.content.IntentFilter;
Kenny Roota02b8b02010-08-05 16:14:17 -070030import android.content.ServiceConnection;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import android.content.pm.PackageManager;
Kenny Root02c87302010-07-01 08:10:18 -070032import android.content.res.ObbInfo;
Mike Lockwood2f6a3882011-05-09 19:08:06 -070033import android.content.res.Resources;
34import android.content.res.TypedArray;
35import android.content.res.XmlResourceParser;
Mike Lockwoodecedfdc2011-06-08 15:11:59 -070036import android.hardware.usb.UsbManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037import android.net.Uri;
Kenny Root02c87302010-07-01 08:10:18 -070038import android.os.Binder;
Kenny Roota02b8b02010-08-05 16:14:17 -070039import android.os.Environment;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080040import android.os.Handler;
Daniel Sandler5f27ef42010-03-16 15:42:02 -040041import android.os.HandlerThread;
Kenny Roota02b8b02010-08-05 16:14:17 -070042import android.os.IBinder;
Daniel Sandler5f27ef42010-03-16 15:42:02 -040043import android.os.Looper;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080044import android.os.Message;
Mike Lockwood2f6a3882011-05-09 19:08:06 -070045import android.os.Parcelable;
San Mehat4270e1e2010-01-29 05:32:19 -080046import android.os.RemoteException;
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -080047import android.os.ServiceManager;
San Mehat207e5382010-02-04 20:46:54 -080048import android.os.SystemClock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import android.os.SystemProperties;
Kenny Roota02b8b02010-08-05 16:14:17 -070050import android.os.storage.IMountService;
51import android.os.storage.IMountServiceListener;
52import android.os.storage.IMountShutdownObserver;
53import android.os.storage.IObbActionListener;
Kenny Rootaf9d6672010-10-08 09:21:39 -070054import android.os.storage.OnObbStateChangeListener;
Kenny Roota02b8b02010-08-05 16:14:17 -070055import android.os.storage.StorageResultCode;
Mike Lockwood2f6a3882011-05-09 19:08:06 -070056import android.os.storage.StorageVolume;
Jason parksf7b3cd42011-01-27 09:28:25 -060057import android.text.TextUtils;
Mike Lockwood2f6a3882011-05-09 19:08:06 -070058import android.util.AttributeSet;
San Mehata5078592010-03-25 09:36:54 -070059import android.util.Slog;
Mike Lockwood2f6a3882011-05-09 19:08:06 -070060import android.util.Xml;
61
62import org.xmlpull.v1.XmlPullParser;
63import org.xmlpull.v1.XmlPullParserException;
Kenny Roota02b8b02010-08-05 16:14:17 -070064
Kenny Root38cf8862010-09-26 14:18:51 -070065import java.io.FileDescriptor;
Kenny Root05105f72010-09-22 17:29:43 -070066import java.io.IOException;
Kenny Root38cf8862010-09-26 14:18:51 -070067import java.io.PrintWriter;
Kenny Root3b1abba2010-10-13 15:00:07 -070068import java.math.BigInteger;
Kenny Root735de3b2010-09-30 14:11:39 -070069import java.security.NoSuchAlgorithmException;
Kenny Root3b1abba2010-10-13 15:00:07 -070070import java.security.spec.InvalidKeySpecException;
71import java.security.spec.KeySpec;
San Mehat22dd86e2010-01-12 12:21:18 -080072import java.util.ArrayList;
Kenny Roota02b8b02010-08-05 16:14:17 -070073import java.util.HashMap;
San Mehat6cdd9c02010-02-09 14:45:20 -080074import java.util.HashSet;
Kenny Root38cf8862010-09-26 14:18:51 -070075import java.util.Iterator;
Kenny Roota02b8b02010-08-05 16:14:17 -070076import java.util.LinkedList;
77import java.util.List;
78import java.util.Map;
Kenny Root38cf8862010-09-26 14:18:51 -070079import java.util.Map.Entry;
Mike Lockwood8fa5f802011-03-24 08:12:30 -070080import java.util.Set;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081
Kenny Root3b1abba2010-10-13 15:00:07 -070082import javax.crypto.SecretKey;
83import javax.crypto.SecretKeyFactory;
84import javax.crypto.spec.PBEKeySpec;
85
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086/**
San Mehatb1043402010-02-05 08:26:50 -080087 * MountService implements back-end services for platform storage
88 * management.
89 * @hide - Applications should use android.os.storage.StorageManager
90 * to access the MountService.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080091 */
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -070092class MountService extends IMountService.Stub
93 implements INativeDaemonConnectorCallbacks, Watchdog.Monitor {
Jason parks5af0b912010-11-29 09:05:25 -060094
San Mehatb1043402010-02-05 08:26:50 -080095 private static final boolean LOCAL_LOGD = false;
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -080096 private static final boolean DEBUG_UNMOUNT = false;
97 private static final boolean DEBUG_EVENTS = false;
Kenny Rootb7db2722011-01-25 16:39:35 -080098 private static final boolean DEBUG_OBB = false;
Kenny Root02c87302010-07-01 08:10:18 -070099
Kenny Root07714d42011-08-17 17:49:28 -0700100 // Disable this since it messes up long-running cryptfs operations.
101 private static final boolean WATCHDOG_ENABLE = false;
102
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103 private static final String TAG = "MountService";
104
Kenny Root305bcbf2010-09-03 07:56:38 -0700105 private static final String VOLD_TAG = "VoldConnector";
106
Kenny Rootcf0b38c2011-03-22 14:17:59 -0700107 /** Maximum number of ASEC containers allowed to be mounted. */
108 private static final int MAX_CONTAINERS = 250;
109
San Mehat4270e1e2010-01-29 05:32:19 -0800110 /*
111 * Internal vold volume state constants
112 */
San Mehat7fd0fee2009-12-17 07:12:23 -0800113 class VolumeState {
114 public static final int Init = -1;
115 public static final int NoMedia = 0;
116 public static final int Idle = 1;
117 public static final int Pending = 2;
118 public static final int Checking = 3;
119 public static final int Mounted = 4;
120 public static final int Unmounting = 5;
121 public static final int Formatting = 6;
122 public static final int Shared = 7;
123 public static final int SharedMnt = 8;
124 }
125
San Mehat4270e1e2010-01-29 05:32:19 -0800126 /*
127 * Internal vold response code constants
128 */
San Mehat22dd86e2010-01-12 12:21:18 -0800129 class VoldResponseCode {
San Mehat4270e1e2010-01-29 05:32:19 -0800130 /*
131 * 100 series - Requestion action was initiated; expect another reply
132 * before proceeding with a new command.
133 */
San Mehat22dd86e2010-01-12 12:21:18 -0800134 public static final int VolumeListResult = 110;
135 public static final int AsecListResult = 111;
San Mehatc1b4ce92010-02-16 17:13:03 -0800136 public static final int StorageUsersListResult = 112;
San Mehat22dd86e2010-01-12 12:21:18 -0800137
San Mehat4270e1e2010-01-29 05:32:19 -0800138 /*
139 * 200 series - Requestion action has been successfully completed.
140 */
141 public static final int ShareStatusResult = 210;
San Mehat22dd86e2010-01-12 12:21:18 -0800142 public static final int AsecPathResult = 211;
San Mehat4270e1e2010-01-29 05:32:19 -0800143 public static final int ShareEnabledResult = 212;
San Mehat22dd86e2010-01-12 12:21:18 -0800144
San Mehat4270e1e2010-01-29 05:32:19 -0800145 /*
146 * 400 series - Command was accepted, but the requested action
147 * did not take place.
148 */
149 public static final int OpFailedNoMedia = 401;
150 public static final int OpFailedMediaBlank = 402;
151 public static final int OpFailedMediaCorrupt = 403;
152 public static final int OpFailedVolNotMounted = 404;
San Mehatd9709982010-02-18 11:43:03 -0800153 public static final int OpFailedStorageBusy = 405;
San Mehat2d66cef2010-03-23 11:12:52 -0700154 public static final int OpFailedStorageNotFound = 406;
San Mehat4270e1e2010-01-29 05:32:19 -0800155
156 /*
157 * 600 series - Unsolicited broadcasts.
158 */
San Mehat22dd86e2010-01-12 12:21:18 -0800159 public static final int VolumeStateChange = 605;
San Mehat22dd86e2010-01-12 12:21:18 -0800160 public static final int VolumeDiskInserted = 630;
161 public static final int VolumeDiskRemoved = 631;
162 public static final int VolumeBadRemoval = 632;
163 }
164
San Mehat4270e1e2010-01-29 05:32:19 -0800165 private Context mContext;
166 private NativeDaemonConnector mConnector;
Mike Lockwood2f6a3882011-05-09 19:08:06 -0700167 private final ArrayList<StorageVolume> mVolumes = new ArrayList<StorageVolume>();
168 private StorageVolume mPrimaryVolume;
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400169 private final HashMap<String, String> mVolumeStates = new HashMap<String, String>();
Mike Lockwooda5250c92011-05-23 13:44:04 -0400170 private final HashMap<String, StorageVolume> mVolumeMap = new HashMap<String, StorageVolume>();
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400171 private String mExternalStoragePath;
San Mehat4270e1e2010-01-29 05:32:19 -0800172 private PackageManagerService mPms;
173 private boolean mUmsEnabling;
Mike Lockwoodecedfdc2011-06-08 15:11:59 -0700174 private boolean mUmsAvailable = false;
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800175 // Used as a lock for methods that register/unregister listeners.
176 final private ArrayList<MountServiceBinderListener> mListeners =
177 new ArrayList<MountServiceBinderListener>();
San Mehat6a965af22010-02-24 17:47:30 -0800178 private boolean mBooted = false;
179 private boolean mReady = false;
180 private boolean mSendUmsConnectedOnBoot = false;
Mike Lockwood03559752010-07-19 18:25:03 -0400181 // true if we should fake MEDIA_MOUNTED state for external storage
182 private boolean mEmulateExternalStorage = false;
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -0800183
San Mehat6cdd9c02010-02-09 14:45:20 -0800184 /**
185 * Private hash of currently mounted secure containers.
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800186 * Used as a lock in methods to manipulate secure containers.
San Mehat6cdd9c02010-02-09 14:45:20 -0800187 */
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800188 final private HashSet<String> mAsecMountSet = new HashSet<String>();
San Mehat6cdd9c02010-02-09 14:45:20 -0800189
Kenny Root02c87302010-07-01 08:10:18 -0700190 /**
Kenny Root3b1abba2010-10-13 15:00:07 -0700191 * The size of the crypto algorithm key in bits for OBB files. Currently
192 * Twofish is used which takes 128-bit keys.
193 */
194 private static final int CRYPTO_ALGORITHM_KEY_SIZE = 128;
195
196 /**
197 * The number of times to run SHA1 in the PBKDF2 function for OBB files.
198 * 1024 is reasonably secure and not too slow.
199 */
200 private static final int PBKDF2_HASH_ROUNDS = 1024;
201
202 /**
Kenny Roota02b8b02010-08-05 16:14:17 -0700203 * Mounted OBB tracking information. Used to track the current state of all
204 * OBBs.
Kenny Root02c87302010-07-01 08:10:18 -0700205 */
Kenny Root735de3b2010-09-30 14:11:39 -0700206 final private Map<IBinder, List<ObbState>> mObbMounts = new HashMap<IBinder, List<ObbState>>();
Kenny Roota02b8b02010-08-05 16:14:17 -0700207 final private Map<String, ObbState> mObbPathToStateMap = new HashMap<String, ObbState>();
208
209 class ObbState implements IBinder.DeathRecipient {
Kenny Rootaf9d6672010-10-08 09:21:39 -0700210 public ObbState(String filename, int callerUid, IObbActionListener token, int nonce)
Kenny Root735de3b2010-09-30 14:11:39 -0700211 throws RemoteException {
Kenny Roota02b8b02010-08-05 16:14:17 -0700212 this.filename = filename;
Kenny Roota02b8b02010-08-05 16:14:17 -0700213 this.callerUid = callerUid;
Kenny Rootaf9d6672010-10-08 09:21:39 -0700214 this.token = token;
215 this.nonce = nonce;
Kenny Roota02b8b02010-08-05 16:14:17 -0700216 }
217
218 // OBB source filename
Kenny Rootaf9d6672010-10-08 09:21:39 -0700219 String filename;
Kenny Roota02b8b02010-08-05 16:14:17 -0700220
221 // Binder.callingUid()
Kenny Root05105f72010-09-22 17:29:43 -0700222 final public int callerUid;
Kenny Roota02b8b02010-08-05 16:14:17 -0700223
Kenny Rootaf9d6672010-10-08 09:21:39 -0700224 // Token of remote Binder caller
225 final IObbActionListener token;
226
227 // Identifier to pass back to the token
228 final int nonce;
Kenny Roota02b8b02010-08-05 16:14:17 -0700229
Kenny Root735de3b2010-09-30 14:11:39 -0700230 public IBinder getBinder() {
231 return token.asBinder();
232 }
233
Kenny Roota02b8b02010-08-05 16:14:17 -0700234 @Override
235 public void binderDied() {
236 ObbAction action = new UnmountObbAction(this, true);
237 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
Kenny Root735de3b2010-09-30 14:11:39 -0700238 }
Kenny Roota02b8b02010-08-05 16:14:17 -0700239
Kenny Root5919ac62010-10-05 09:49:40 -0700240 public void link() throws RemoteException {
241 getBinder().linkToDeath(this, 0);
242 }
243
244 public void unlink() {
Kenny Root735de3b2010-09-30 14:11:39 -0700245 getBinder().unlinkToDeath(this, 0);
Kenny Roota02b8b02010-08-05 16:14:17 -0700246 }
Kenny Root38cf8862010-09-26 14:18:51 -0700247
248 @Override
249 public String toString() {
250 StringBuilder sb = new StringBuilder("ObbState{");
251 sb.append("filename=");
252 sb.append(filename);
253 sb.append(",token=");
254 sb.append(token.toString());
255 sb.append(",callerUid=");
256 sb.append(callerUid);
Kenny Root38cf8862010-09-26 14:18:51 -0700257 sb.append('}');
258 return sb.toString();
259 }
Kenny Roota02b8b02010-08-05 16:14:17 -0700260 }
261
262 // OBB Action Handler
263 final private ObbActionHandler mObbActionHandler;
264
265 // OBB action handler messages
266 private static final int OBB_RUN_ACTION = 1;
267 private static final int OBB_MCS_BOUND = 2;
268 private static final int OBB_MCS_UNBIND = 3;
269 private static final int OBB_MCS_RECONNECT = 4;
Kenny Rootaf9d6672010-10-08 09:21:39 -0700270 private static final int OBB_FLUSH_MOUNT_STATE = 5;
Kenny Roota02b8b02010-08-05 16:14:17 -0700271
272 /*
273 * Default Container Service information
274 */
275 static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
276 "com.android.defcontainer", "com.android.defcontainer.DefaultContainerService");
277
278 final private DefaultContainerConnection mDefContainerConn = new DefaultContainerConnection();
279
280 class DefaultContainerConnection implements ServiceConnection {
281 public void onServiceConnected(ComponentName name, IBinder service) {
282 if (DEBUG_OBB)
283 Slog.i(TAG, "onServiceConnected");
284 IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(service);
285 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_MCS_BOUND, imcs));
286 }
287
288 public void onServiceDisconnected(ComponentName name) {
289 if (DEBUG_OBB)
290 Slog.i(TAG, "onServiceDisconnected");
291 }
292 };
293
294 // Used in the ObbActionHandler
295 private IMediaContainerService mContainerService = null;
Kenny Root02c87302010-07-01 08:10:18 -0700296
297 // Handler messages
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800298 private static final int H_UNMOUNT_PM_UPDATE = 1;
299 private static final int H_UNMOUNT_PM_DONE = 2;
300 private static final int H_UNMOUNT_MS = 3;
301 private static final int RETRY_UNMOUNT_DELAY = 30; // in ms
302 private static final int MAX_UNMOUNT_RETRIES = 4;
303
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800304 class UnmountCallBack {
Kenny Root05105f72010-09-22 17:29:43 -0700305 final String path;
306 final boolean force;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800307 int retries;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800308
309 UnmountCallBack(String path, boolean force) {
310 retries = 0;
311 this.path = path;
312 this.force = force;
313 }
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800314
315 void handleFinished() {
San Mehata5078592010-03-25 09:36:54 -0700316 if (DEBUG_UNMOUNT) Slog.i(TAG, "Unmounting " + path);
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800317 doUnmountVolume(path, true);
318 }
319 }
320
321 class UmsEnableCallBack extends UnmountCallBack {
Kenny Root05105f72010-09-22 17:29:43 -0700322 final String method;
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800323
324 UmsEnableCallBack(String path, String method, boolean force) {
325 super(path, force);
326 this.method = method;
327 }
328
329 @Override
330 void handleFinished() {
331 super.handleFinished();
332 doShareUnshareVolume(path, method, true);
333 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800334 }
335
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800336 class ShutdownCallBack extends UnmountCallBack {
337 IMountShutdownObserver observer;
338 ShutdownCallBack(String path, IMountShutdownObserver observer) {
339 super(path, true);
340 this.observer = observer;
341 }
342
343 @Override
344 void handleFinished() {
345 int ret = doUnmountVolume(path, true);
346 if (observer != null) {
347 try {
348 observer.onShutDownComplete(ret);
349 } catch (RemoteException e) {
San Mehata5078592010-03-25 09:36:54 -0700350 Slog.w(TAG, "RemoteException when shutting down");
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800351 }
352 }
353 }
354 }
355
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400356 class MountServiceHandler extends Handler {
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800357 ArrayList<UnmountCallBack> mForceUnmounts = new ArrayList<UnmountCallBack>();
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700358 boolean mUpdatingStatus = false;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800359
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400360 MountServiceHandler(Looper l) {
361 super(l);
362 }
363
Jason parks5af0b912010-11-29 09:05:25 -0600364 @Override
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800365 public void handleMessage(Message msg) {
366 switch (msg.what) {
367 case H_UNMOUNT_PM_UPDATE: {
San Mehata5078592010-03-25 09:36:54 -0700368 if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_UPDATE");
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800369 UnmountCallBack ucb = (UnmountCallBack) msg.obj;
370 mForceUnmounts.add(ucb);
San Mehata5078592010-03-25 09:36:54 -0700371 if (DEBUG_UNMOUNT) Slog.i(TAG, " registered = " + mUpdatingStatus);
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800372 // Register only if needed.
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700373 if (!mUpdatingStatus) {
San Mehata5078592010-03-25 09:36:54 -0700374 if (DEBUG_UNMOUNT) Slog.i(TAG, "Updating external media status on PackageManager");
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700375 mUpdatingStatus = true;
376 mPms.updateExternalMediaStatus(false, true);
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800377 }
378 break;
379 }
380 case H_UNMOUNT_PM_DONE: {
San Mehata5078592010-03-25 09:36:54 -0700381 if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_DONE");
San Mehata5078592010-03-25 09:36:54 -0700382 if (DEBUG_UNMOUNT) Slog.i(TAG, "Updated status. Processing requests");
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700383 mUpdatingStatus = false;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800384 int size = mForceUnmounts.size();
385 int sizeArr[] = new int[size];
386 int sizeArrN = 0;
Suchi Amalapurapu7af074a2010-04-05 16:46:32 -0700387 // Kill processes holding references first
388 ActivityManagerService ams = (ActivityManagerService)
389 ServiceManager.getService("activity");
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800390 for (int i = 0; i < size; i++) {
391 UnmountCallBack ucb = mForceUnmounts.get(i);
392 String path = ucb.path;
393 boolean done = false;
394 if (!ucb.force) {
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800395 done = true;
396 } else {
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800397 int pids[] = getStorageUsers(path);
398 if (pids == null || pids.length == 0) {
399 done = true;
400 } else {
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800401 // Eliminate system process here?
Dianne Hackborn64825172011-03-02 21:32:58 -0800402 ams.killPids(pids, "unmount media", true);
Suchi Amalapurapu7af074a2010-04-05 16:46:32 -0700403 // Confirm if file references have been freed.
404 pids = getStorageUsers(path);
405 if (pids == null || pids.length == 0) {
406 done = true;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800407 }
408 }
409 }
Suchi Amalapurapu7af074a2010-04-05 16:46:32 -0700410 if (!done && (ucb.retries < MAX_UNMOUNT_RETRIES)) {
411 // Retry again
412 Slog.i(TAG, "Retrying to kill storage users again");
413 mHandler.sendMessageDelayed(
414 mHandler.obtainMessage(H_UNMOUNT_PM_DONE,
415 ucb.retries++),
416 RETRY_UNMOUNT_DELAY);
417 } else {
418 if (ucb.retries >= MAX_UNMOUNT_RETRIES) {
419 Slog.i(TAG, "Failed to unmount media inspite of " +
420 MAX_UNMOUNT_RETRIES + " retries. Forcibly killing processes now");
421 }
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800422 sizeArr[sizeArrN++] = i;
423 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_MS,
424 ucb));
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800425 }
426 }
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800427 // Remove already processed elements from list.
428 for (int i = (sizeArrN-1); i >= 0; i--) {
429 mForceUnmounts.remove(sizeArr[i]);
430 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800431 break;
432 }
433 case H_UNMOUNT_MS : {
San Mehata5078592010-03-25 09:36:54 -0700434 if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_MS");
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800435 UnmountCallBack ucb = (UnmountCallBack) msg.obj;
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800436 ucb.handleFinished();
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800437 break;
438 }
439 }
440 }
441 };
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400442 final private HandlerThread mHandlerThread;
443 final private Handler mHandler;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800444
San Mehat207e5382010-02-04 20:46:54 -0800445 private void waitForReady() {
446 while (mReady == false) {
447 for (int retries = 5; retries > 0; retries--) {
448 if (mReady) {
449 return;
450 }
451 SystemClock.sleep(1000);
452 }
San Mehata5078592010-03-25 09:36:54 -0700453 Slog.w(TAG, "Waiting too long for mReady!");
San Mehat207e5382010-02-04 20:46:54 -0800454 }
San Mehat1f6301e2010-01-07 22:40:27 -0800455 }
Kenny Root02c87302010-07-01 08:10:18 -0700456
Ben Komalo444eca22011-09-01 15:17:44 -0700457 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
Jason parks5af0b912010-11-29 09:05:25 -0600458 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800459 public void onReceive(Context context, Intent intent) {
San Mehat91c77612010-01-07 10:39:41 -0800460 String action = intent.getAction();
461
462 if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
San Mehat207e5382010-02-04 20:46:54 -0800463 mBooted = true;
San Mehat22dd86e2010-01-12 12:21:18 -0800464
Marco Nelissenc34ebce2010-02-18 13:39:41 -0800465 /*
466 * In the simulator, we need to broadcast a volume mounted event
467 * to make the media scanner run.
468 */
469 if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
Mike Lockwood84338c42011-04-05 10:05:47 -0400470 notifyVolumeStateChange(null, "/sdcard", VolumeState.NoMedia,
471 VolumeState.Mounted);
Marco Nelissenc34ebce2010-02-18 13:39:41 -0800472 return;
473 }
San Mehatfafb0412010-02-18 19:40:04 -0800474 new Thread() {
Jason parks5af0b912010-11-29 09:05:25 -0600475 @Override
San Mehatfafb0412010-02-18 19:40:04 -0800476 public void run() {
477 try {
Mike Lockwood84338c42011-04-05 10:05:47 -0400478 // it is not safe to call vold with mVolumeStates locked
479 // so we make a copy of the paths and states and process them
480 // outside the lock
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -0700481 String[] paths;
482 String[] states;
Mike Lockwood84338c42011-04-05 10:05:47 -0400483 int count;
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400484 synchronized (mVolumeStates) {
Mike Lockwood84338c42011-04-05 10:05:47 -0400485 Set<String> keys = mVolumeStates.keySet();
486 count = keys.size();
Ben Komalo444eca22011-09-01 15:17:44 -0700487 paths = keys.toArray(new String[count]);
Mike Lockwood84338c42011-04-05 10:05:47 -0400488 states = new String[count];
489 for (int i = 0; i < count; i++) {
490 states[i] = mVolumeStates.get(paths[i]);
491 }
492 }
San Mehat6a254402010-03-22 10:21:00 -0700493
Mike Lockwood84338c42011-04-05 10:05:47 -0400494 for (int i = 0; i < count; i++) {
495 String path = paths[i];
496 String state = states[i];
497
498 if (state.equals(Environment.MEDIA_UNMOUNTED)) {
499 int rc = doMountVolume(path);
500 if (rc != StorageResultCode.OperationSucceeded) {
501 Slog.e(TAG, String.format("Boot-time mount failed (%d)",
502 rc));
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400503 }
Mike Lockwood84338c42011-04-05 10:05:47 -0400504 } else if (state.equals(Environment.MEDIA_SHARED)) {
505 /*
506 * Bootstrap UMS enabled state since vold indicates
507 * the volume is shared (runtime restart while ums enabled)
508 */
509 notifyVolumeStateChange(null, path, VolumeState.NoMedia,
510 VolumeState.Shared);
San Mehatfafb0412010-02-18 19:40:04 -0800511 }
512 }
San Mehat6a254402010-03-22 10:21:00 -0700513
Mike Lockwood80e0a412011-04-05 10:21:51 -0400514 /* notify external storage has mounted to trigger media scanner */
515 if (mEmulateExternalStorage) {
516 notifyVolumeStateChange(null,
517 Environment.getExternalStorageDirectory().getPath(),
518 VolumeState.NoMedia, VolumeState.Mounted);
519 }
520
San Mehat6a965af22010-02-24 17:47:30 -0800521 /*
San Mehat6a254402010-03-22 10:21:00 -0700522 * If UMS was connected on boot, send the connected event
San Mehat6a965af22010-02-24 17:47:30 -0800523 * now that we're up.
524 */
525 if (mSendUmsConnectedOnBoot) {
526 sendUmsIntent(true);
527 mSendUmsConnectedOnBoot = false;
528 }
San Mehatfafb0412010-02-18 19:40:04 -0800529 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700530 Slog.e(TAG, "Boot-time mount exception", ex);
San Mehatfafb0412010-02-18 19:40:04 -0800531 }
San Mehat207e5382010-02-04 20:46:54 -0800532 }
San Mehatfafb0412010-02-18 19:40:04 -0800533 }.start();
Mike Lockwoodecedfdc2011-06-08 15:11:59 -0700534 } else if (action.equals(UsbManager.ACTION_USB_STATE)) {
535 boolean available = (intent.getBooleanExtra(UsbManager.USB_CONNECTED, false) &&
536 intent.getBooleanExtra(UsbManager.USB_FUNCTION_MASS_STORAGE, false));
537 notifyShareAvailabilityChange(available);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800538 }
539 }
540 };
San Mehat4270e1e2010-01-29 05:32:19 -0800541 private final class MountServiceBinderListener implements IBinder.DeathRecipient {
542 final IMountServiceListener mListener;
543
544 MountServiceBinderListener(IMountServiceListener listener) {
545 mListener = listener;
Kenny Root02c87302010-07-01 08:10:18 -0700546
San Mehat91c77612010-01-07 10:39:41 -0800547 }
548
San Mehat4270e1e2010-01-29 05:32:19 -0800549 public void binderDied() {
San Mehata5078592010-03-25 09:36:54 -0700550 if (LOCAL_LOGD) Slog.d(TAG, "An IMountServiceListener has died!");
Kenny Roota02b8b02010-08-05 16:14:17 -0700551 synchronized (mListeners) {
San Mehat4270e1e2010-01-29 05:32:19 -0800552 mListeners.remove(this);
553 mListener.asBinder().unlinkToDeath(this, 0);
554 }
555 }
556 }
557
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800558 private void doShareUnshareVolume(String path, String method, boolean enable) {
San Mehat4270e1e2010-01-29 05:32:19 -0800559 // TODO: Add support for multiple share methods
560 if (!method.equals("ums")) {
561 throw new IllegalArgumentException(String.format("Method %s not supported", method));
562 }
563
San Mehat4270e1e2010-01-29 05:32:19 -0800564 try {
565 mConnector.doCommand(String.format(
566 "volume %sshare %s %s", (enable ? "" : "un"), path, method));
567 } catch (NativeDaemonConnectorException e) {
San Mehata5078592010-03-25 09:36:54 -0700568 Slog.e(TAG, "Failed to share/unshare", e);
San Mehat4270e1e2010-01-29 05:32:19 -0800569 }
San Mehat4270e1e2010-01-29 05:32:19 -0800570 }
571
San Mehat207e5382010-02-04 20:46:54 -0800572 private void updatePublicVolumeState(String path, String state) {
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400573 String oldState;
574 synchronized(mVolumeStates) {
575 oldState = mVolumeStates.put(path, state);
576 }
577 if (state.equals(oldState)) {
578 Slog.w(TAG, String.format("Duplicate state transition (%s -> %s) for %s",
579 state, state, path));
San Mehat4270e1e2010-01-29 05:32:19 -0800580 return;
581 }
San Mehatb1043402010-02-05 08:26:50 -0800582
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400583 Slog.d(TAG, "volume state changed for " + path + " (" + oldState + " -> " + state + ")");
Kenny Rootaf9d6672010-10-08 09:21:39 -0700584
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400585 if (path.equals(mExternalStoragePath)) {
586 // Update state on PackageManager, but only of real events
587 if (!mEmulateExternalStorage) {
588 if (Environment.MEDIA_UNMOUNTED.equals(state)) {
589 mPms.updateExternalMediaStatus(false, false);
590
591 /*
592 * Some OBBs might have been unmounted when this volume was
593 * unmounted, so send a message to the handler to let it know to
594 * remove those from the list of mounted OBBS.
595 */
596 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(
597 OBB_FLUSH_MOUNT_STATE, path));
598 } else if (Environment.MEDIA_MOUNTED.equals(state)) {
599 mPms.updateExternalMediaStatus(true, false);
600 }
Mike Lockwood03559752010-07-19 18:25:03 -0400601 }
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800602 }
San Mehat4270e1e2010-01-29 05:32:19 -0800603 synchronized (mListeners) {
604 for (int i = mListeners.size() -1; i >= 0; i--) {
605 MountServiceBinderListener bl = mListeners.get(i);
606 try {
San Mehatb1043402010-02-05 08:26:50 -0800607 bl.mListener.onStorageStateChanged(path, oldState, state);
San Mehat4270e1e2010-01-29 05:32:19 -0800608 } catch (RemoteException rex) {
San Mehata5078592010-03-25 09:36:54 -0700609 Slog.e(TAG, "Listener dead");
San Mehat4270e1e2010-01-29 05:32:19 -0800610 mListeners.remove(i);
611 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700612 Slog.e(TAG, "Listener failed", ex);
San Mehat4270e1e2010-01-29 05:32:19 -0800613 }
614 }
615 }
616 }
617
618 /**
619 *
620 * Callback from NativeDaemonConnector
621 */
622 public void onDaemonConnected() {
623 /*
624 * Since we'll be calling back into the NativeDaemonConnector,
625 * we need to do our work in a new thread.
626 */
627 new Thread() {
Jason parks5af0b912010-11-29 09:05:25 -0600628 @Override
San Mehat4270e1e2010-01-29 05:32:19 -0800629 public void run() {
630 /**
631 * Determine media state and UMS detection status
632 */
San Mehat4270e1e2010-01-29 05:32:19 -0800633 try {
634 String[] vols = mConnector.doListCommand(
635 "volume list", VoldResponseCode.VolumeListResult);
636 for (String volstr : vols) {
637 String[] tok = volstr.split(" ");
638 // FMT: <label> <mountpoint> <state>
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400639 String path = tok[1];
640 String state = Environment.MEDIA_REMOVED;
641
San Mehat4270e1e2010-01-29 05:32:19 -0800642 int st = Integer.parseInt(tok[2]);
643 if (st == VolumeState.NoMedia) {
644 state = Environment.MEDIA_REMOVED;
645 } else if (st == VolumeState.Idle) {
San Mehat207e5382010-02-04 20:46:54 -0800646 state = Environment.MEDIA_UNMOUNTED;
San Mehat4270e1e2010-01-29 05:32:19 -0800647 } else if (st == VolumeState.Mounted) {
648 state = Environment.MEDIA_MOUNTED;
San Mehata5078592010-03-25 09:36:54 -0700649 Slog.i(TAG, "Media already mounted on daemon connection");
San Mehat4270e1e2010-01-29 05:32:19 -0800650 } else if (st == VolumeState.Shared) {
651 state = Environment.MEDIA_SHARED;
San Mehata5078592010-03-25 09:36:54 -0700652 Slog.i(TAG, "Media shared on daemon connection");
San Mehat4270e1e2010-01-29 05:32:19 -0800653 } else {
654 throw new Exception(String.format("Unexpected state %d", st));
655 }
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400656
657 if (state != null) {
658 if (DEBUG_EVENTS) Slog.i(TAG, "Updating valid state " + state);
659 updatePublicVolumeState(path, state);
660 }
San Mehat4270e1e2010-01-29 05:32:19 -0800661 }
662 } catch (Exception e) {
San Mehata5078592010-03-25 09:36:54 -0700663 Slog.e(TAG, "Error processing initial volume state", e);
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400664 updatePublicVolumeState(mExternalStoragePath, Environment.MEDIA_REMOVED);
San Mehat4270e1e2010-01-29 05:32:19 -0800665 }
666
San Mehat207e5382010-02-04 20:46:54 -0800667 /*
Jason parks9ed98bc2011-01-17 09:58:35 -0600668 * Now that we've done our initialization, release
San Mehat207e5382010-02-04 20:46:54 -0800669 * the hounds!
670 */
671 mReady = true;
San Mehat4270e1e2010-01-29 05:32:19 -0800672 }
673 }.start();
674 }
675
676 /**
San Mehat4270e1e2010-01-29 05:32:19 -0800677 * Callback from NativeDaemonConnector
678 */
679 public boolean onEvent(int code, String raw, String[] cooked) {
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800680 if (DEBUG_EVENTS) {
681 StringBuilder builder = new StringBuilder();
682 builder.append("onEvent::");
683 builder.append(" raw= " + raw);
684 if (cooked != null) {
685 builder.append(" cooked = " );
686 for (String str : cooked) {
687 builder.append(" " + str);
688 }
689 }
San Mehata5078592010-03-25 09:36:54 -0700690 Slog.i(TAG, builder.toString());
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800691 }
San Mehat4270e1e2010-01-29 05:32:19 -0800692 if (code == VoldResponseCode.VolumeStateChange) {
693 /*
694 * One of the volumes we're managing has changed state.
695 * Format: "NNN Volume <label> <path> state changed
696 * from <old_#> (<old_str>) to <new_#> (<new_str>)"
697 */
698 notifyVolumeStateChange(
699 cooked[2], cooked[3], Integer.parseInt(cooked[7]),
700 Integer.parseInt(cooked[10]));
San Mehat4270e1e2010-01-29 05:32:19 -0800701 } else if ((code == VoldResponseCode.VolumeDiskInserted) ||
702 (code == VoldResponseCode.VolumeDiskRemoved) ||
703 (code == VoldResponseCode.VolumeBadRemoval)) {
704 // FMT: NNN Volume <label> <mountpoint> disk inserted (<major>:<minor>)
705 // FMT: NNN Volume <label> <mountpoint> disk removed (<major>:<minor>)
706 // FMT: NNN Volume <label> <mountpoint> bad removal (<major>:<minor>)
Mike Lockwooda5250c92011-05-23 13:44:04 -0400707 String action = null;
San Mehat4270e1e2010-01-29 05:32:19 -0800708 final String label = cooked[2];
709 final String path = cooked[3];
710 int major = -1;
711 int minor = -1;
712
713 try {
714 String devComp = cooked[6].substring(1, cooked[6].length() -1);
715 String[] devTok = devComp.split(":");
716 major = Integer.parseInt(devTok[0]);
717 minor = Integer.parseInt(devTok[1]);
718 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700719 Slog.e(TAG, "Failed to parse major/minor", ex);
San Mehat4270e1e2010-01-29 05:32:19 -0800720 }
721
San Mehat4270e1e2010-01-29 05:32:19 -0800722 if (code == VoldResponseCode.VolumeDiskInserted) {
723 new Thread() {
Jason parks5af0b912010-11-29 09:05:25 -0600724 @Override
San Mehat4270e1e2010-01-29 05:32:19 -0800725 public void run() {
726 try {
727 int rc;
San Mehatb1043402010-02-05 08:26:50 -0800728 if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {
San Mehata5078592010-03-25 09:36:54 -0700729 Slog.w(TAG, String.format("Insertion mount failed (%d)", rc));
San Mehat4270e1e2010-01-29 05:32:19 -0800730 }
731 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700732 Slog.w(TAG, "Failed to mount media on insertion", ex);
San Mehat4270e1e2010-01-29 05:32:19 -0800733 }
734 }
735 }.start();
736 } else if (code == VoldResponseCode.VolumeDiskRemoved) {
737 /*
738 * This event gets trumped if we're already in BAD_REMOVAL state
739 */
740 if (getVolumeState(path).equals(Environment.MEDIA_BAD_REMOVAL)) {
741 return true;
742 }
743 /* Send the media unmounted event first */
San Mehata5078592010-03-25 09:36:54 -0700744 if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
San Mehat4270e1e2010-01-29 05:32:19 -0800745 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
Mike Lockwooda5250c92011-05-23 13:44:04 -0400746 sendStorageIntent(Environment.MEDIA_UNMOUNTED, path);
San Mehat4270e1e2010-01-29 05:32:19 -0800747
San Mehata5078592010-03-25 09:36:54 -0700748 if (DEBUG_EVENTS) Slog.i(TAG, "Sending media removed");
San Mehat4270e1e2010-01-29 05:32:19 -0800749 updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
Mike Lockwooda5250c92011-05-23 13:44:04 -0400750 action = Intent.ACTION_MEDIA_REMOVED;
San Mehat4270e1e2010-01-29 05:32:19 -0800751 } else if (code == VoldResponseCode.VolumeBadRemoval) {
San Mehata5078592010-03-25 09:36:54 -0700752 if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
San Mehat4270e1e2010-01-29 05:32:19 -0800753 /* Send the media unmounted event first */
754 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
Mike Lockwooda5250c92011-05-23 13:44:04 -0400755 action = Intent.ACTION_MEDIA_UNMOUNTED;
San Mehat4270e1e2010-01-29 05:32:19 -0800756
San Mehata5078592010-03-25 09:36:54 -0700757 if (DEBUG_EVENTS) Slog.i(TAG, "Sending media bad removal");
San Mehat4270e1e2010-01-29 05:32:19 -0800758 updatePublicVolumeState(path, Environment.MEDIA_BAD_REMOVAL);
Mike Lockwooda5250c92011-05-23 13:44:04 -0400759 action = Intent.ACTION_MEDIA_BAD_REMOVAL;
San Mehat4270e1e2010-01-29 05:32:19 -0800760 } else {
San Mehata5078592010-03-25 09:36:54 -0700761 Slog.e(TAG, String.format("Unknown code {%d}", code));
San Mehat4270e1e2010-01-29 05:32:19 -0800762 }
Mike Lockwooda5250c92011-05-23 13:44:04 -0400763
764 if (action != null) {
765 sendStorageIntent(action, path);
766 }
San Mehat4270e1e2010-01-29 05:32:19 -0800767 } else {
768 return false;
769 }
770
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400771 return true;
San Mehat4270e1e2010-01-29 05:32:19 -0800772 }
773
San Mehat207e5382010-02-04 20:46:54 -0800774 private void notifyVolumeStateChange(String label, String path, int oldState, int newState) {
San Mehat4270e1e2010-01-29 05:32:19 -0800775 String vs = getVolumeState(path);
San Mehata5078592010-03-25 09:36:54 -0700776 if (DEBUG_EVENTS) Slog.i(TAG, "notifyVolumeStateChanged::" + vs);
San Mehat4270e1e2010-01-29 05:32:19 -0800777
Mike Lockwooda5250c92011-05-23 13:44:04 -0400778 String action = null;
San Mehat4270e1e2010-01-29 05:32:19 -0800779
Mike Lockwoodbf2dd442010-03-03 06:16:52 -0500780 if (oldState == VolumeState.Shared && newState != oldState) {
San Mehata5078592010-03-25 09:36:54 -0700781 if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_UNSHARED intent");
Mike Lockwooda5250c92011-05-23 13:44:04 -0400782 sendStorageIntent(Intent.ACTION_MEDIA_UNSHARED, path);
Mike Lockwoodbf2dd442010-03-03 06:16:52 -0500783 }
784
San Mehat4270e1e2010-01-29 05:32:19 -0800785 if (newState == VolumeState.Init) {
786 } else if (newState == VolumeState.NoMedia) {
787 // NoMedia is handled via Disk Remove events
788 } else if (newState == VolumeState.Idle) {
789 /*
790 * Don't notify if we're in BAD_REMOVAL, NOFS, UNMOUNTABLE, or
791 * if we're in the process of enabling UMS
792 */
793 if (!vs.equals(
794 Environment.MEDIA_BAD_REMOVAL) && !vs.equals(
795 Environment.MEDIA_NOFS) && !vs.equals(
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800796 Environment.MEDIA_UNMOUNTABLE) && !getUmsEnabling()) {
San Mehata5078592010-03-25 09:36:54 -0700797 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state for media bad removal nofs and unmountable");
San Mehat4270e1e2010-01-29 05:32:19 -0800798 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
Mike Lockwooda5250c92011-05-23 13:44:04 -0400799 action = Intent.ACTION_MEDIA_UNMOUNTED;
San Mehat4270e1e2010-01-29 05:32:19 -0800800 }
801 } else if (newState == VolumeState.Pending) {
802 } else if (newState == VolumeState.Checking) {
San Mehata5078592010-03-25 09:36:54 -0700803 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state checking");
San Mehat4270e1e2010-01-29 05:32:19 -0800804 updatePublicVolumeState(path, Environment.MEDIA_CHECKING);
Mike Lockwooda5250c92011-05-23 13:44:04 -0400805 action = Intent.ACTION_MEDIA_CHECKING;
San Mehat4270e1e2010-01-29 05:32:19 -0800806 } else if (newState == VolumeState.Mounted) {
San Mehata5078592010-03-25 09:36:54 -0700807 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state mounted");
San Mehat4270e1e2010-01-29 05:32:19 -0800808 updatePublicVolumeState(path, Environment.MEDIA_MOUNTED);
Mike Lockwooda5250c92011-05-23 13:44:04 -0400809 action = Intent.ACTION_MEDIA_MOUNTED;
San Mehat4270e1e2010-01-29 05:32:19 -0800810 } else if (newState == VolumeState.Unmounting) {
Mike Lockwooda5250c92011-05-23 13:44:04 -0400811 action = Intent.ACTION_MEDIA_EJECT;
San Mehat4270e1e2010-01-29 05:32:19 -0800812 } else if (newState == VolumeState.Formatting) {
813 } else if (newState == VolumeState.Shared) {
San Mehata5078592010-03-25 09:36:54 -0700814 if (DEBUG_EVENTS) Slog.i(TAG, "Updating volume state media mounted");
San Mehat4270e1e2010-01-29 05:32:19 -0800815 /* Send the media unmounted event first */
816 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
Mike Lockwooda5250c92011-05-23 13:44:04 -0400817 sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, path);
San Mehat4270e1e2010-01-29 05:32:19 -0800818
San Mehata5078592010-03-25 09:36:54 -0700819 if (DEBUG_EVENTS) Slog.i(TAG, "Updating media shared");
San Mehat4270e1e2010-01-29 05:32:19 -0800820 updatePublicVolumeState(path, Environment.MEDIA_SHARED);
Mike Lockwooda5250c92011-05-23 13:44:04 -0400821 action = Intent.ACTION_MEDIA_SHARED;
San Mehata5078592010-03-25 09:36:54 -0700822 if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_SHARED intent");
San Mehat4270e1e2010-01-29 05:32:19 -0800823 } else if (newState == VolumeState.SharedMnt) {
San Mehata5078592010-03-25 09:36:54 -0700824 Slog.e(TAG, "Live shared mounts not supported yet!");
San Mehat4270e1e2010-01-29 05:32:19 -0800825 return;
826 } else {
San Mehata5078592010-03-25 09:36:54 -0700827 Slog.e(TAG, "Unhandled VolumeState {" + newState + "}");
San Mehat4270e1e2010-01-29 05:32:19 -0800828 }
829
Mike Lockwooda5250c92011-05-23 13:44:04 -0400830 if (action != null) {
831 sendStorageIntent(action, path);
San Mehat4270e1e2010-01-29 05:32:19 -0800832 }
833 }
834
San Mehat207e5382010-02-04 20:46:54 -0800835 private int doMountVolume(String path) {
San Mehatb1043402010-02-05 08:26:50 -0800836 int rc = StorageResultCode.OperationSucceeded;
San Mehat207e5382010-02-04 20:46:54 -0800837
San Mehata5078592010-03-25 09:36:54 -0700838 if (DEBUG_EVENTS) Slog.i(TAG, "doMountVolume: Mouting " + path);
San Mehat207e5382010-02-04 20:46:54 -0800839 try {
840 mConnector.doCommand(String.format("volume mount %s", path));
841 } catch (NativeDaemonConnectorException e) {
842 /*
843 * Mount failed for some reason
844 */
Mike Lockwooda5250c92011-05-23 13:44:04 -0400845 String action = null;
San Mehat207e5382010-02-04 20:46:54 -0800846 int code = e.getCode();
847 if (code == VoldResponseCode.OpFailedNoMedia) {
848 /*
849 * Attempt to mount but no media inserted
850 */
San Mehatb1043402010-02-05 08:26:50 -0800851 rc = StorageResultCode.OperationFailedNoMedia;
San Mehat207e5382010-02-04 20:46:54 -0800852 } else if (code == VoldResponseCode.OpFailedMediaBlank) {
San Mehata5078592010-03-25 09:36:54 -0700853 if (DEBUG_EVENTS) Slog.i(TAG, " updating volume state :: media nofs");
San Mehat207e5382010-02-04 20:46:54 -0800854 /*
855 * Media is blank or does not contain a supported filesystem
856 */
857 updatePublicVolumeState(path, Environment.MEDIA_NOFS);
Mike Lockwooda5250c92011-05-23 13:44:04 -0400858 action = Intent.ACTION_MEDIA_NOFS;
San Mehatb1043402010-02-05 08:26:50 -0800859 rc = StorageResultCode.OperationFailedMediaBlank;
San Mehat207e5382010-02-04 20:46:54 -0800860 } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
San Mehata5078592010-03-25 09:36:54 -0700861 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state media corrupt");
San Mehat207e5382010-02-04 20:46:54 -0800862 /*
863 * Volume consistency check failed
864 */
865 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTABLE);
Mike Lockwooda5250c92011-05-23 13:44:04 -0400866 action = Intent.ACTION_MEDIA_UNMOUNTABLE;
San Mehatb1043402010-02-05 08:26:50 -0800867 rc = StorageResultCode.OperationFailedMediaCorrupt;
San Mehat207e5382010-02-04 20:46:54 -0800868 } else {
San Mehatb1043402010-02-05 08:26:50 -0800869 rc = StorageResultCode.OperationFailedInternalError;
San Mehat207e5382010-02-04 20:46:54 -0800870 }
871
872 /*
873 * Send broadcast intent (if required for the failure)
874 */
Mike Lockwooda5250c92011-05-23 13:44:04 -0400875 if (action != null) {
876 sendStorageIntent(action, path);
San Mehat207e5382010-02-04 20:46:54 -0800877 }
878 }
879
880 return rc;
881 }
882
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800883 /*
884 * If force is not set, we do not unmount if there are
885 * processes holding references to the volume about to be unmounted.
886 * If force is set, all the processes holding references need to be
887 * killed via the ActivityManager before actually unmounting the volume.
888 * This might even take a while and might be retried after timed delays
889 * to make sure we dont end up in an instable state and kill some core
890 * processes.
891 */
San Mehatd9709982010-02-18 11:43:03 -0800892 private int doUnmountVolume(String path, boolean force) {
San Mehat59443a62010-02-09 13:28:45 -0800893 if (!getVolumeState(path).equals(Environment.MEDIA_MOUNTED)) {
San Mehat207e5382010-02-04 20:46:54 -0800894 return VoldResponseCode.OpFailedVolNotMounted;
895 }
Kenny Rootaa485402010-09-14 14:49:41 -0700896
897 /*
898 * Force a GC to make sure AssetManagers in other threads of the
899 * system_server are cleaned up. We have to do this since AssetManager
900 * instances are kept as a WeakReference and it's possible we have files
901 * open on the external storage.
902 */
903 Runtime.getRuntime().gc();
904
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800905 // Redundant probably. But no harm in updating state again.
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700906 mPms.updateExternalMediaStatus(false, false);
San Mehat207e5382010-02-04 20:46:54 -0800907 try {
San Mehatd9709982010-02-18 11:43:03 -0800908 mConnector.doCommand(String.format(
909 "volume unmount %s%s", path, (force ? " force" : "")));
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700910 // We unmounted the volume. None of the asec containers are available now.
911 synchronized (mAsecMountSet) {
912 mAsecMountSet.clear();
913 }
San Mehatb1043402010-02-05 08:26:50 -0800914 return StorageResultCode.OperationSucceeded;
San Mehat207e5382010-02-04 20:46:54 -0800915 } catch (NativeDaemonConnectorException e) {
916 // Don't worry about mismatch in PackageManager since the
917 // call back will handle the status changes any way.
918 int code = e.getCode();
919 if (code == VoldResponseCode.OpFailedVolNotMounted) {
San Mehata181b212010-02-11 06:50:20 -0800920 return StorageResultCode.OperationFailedStorageNotMounted;
San Mehatd9709982010-02-18 11:43:03 -0800921 } else if (code == VoldResponseCode.OpFailedStorageBusy) {
922 return StorageResultCode.OperationFailedStorageBusy;
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
929 private int doFormatVolume(String path) {
930 try {
931 String cmd = String.format("volume format %s", path);
932 mConnector.doCommand(cmd);
San Mehatb1043402010-02-05 08:26:50 -0800933 return StorageResultCode.OperationSucceeded;
San Mehat207e5382010-02-04 20:46:54 -0800934 } catch (NativeDaemonConnectorException e) {
935 int code = e.getCode();
936 if (code == VoldResponseCode.OpFailedNoMedia) {
San Mehatb1043402010-02-05 08:26:50 -0800937 return StorageResultCode.OperationFailedNoMedia;
San Mehat207e5382010-02-04 20:46:54 -0800938 } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
San Mehatb1043402010-02-05 08:26:50 -0800939 return StorageResultCode.OperationFailedMediaCorrupt;
San Mehat207e5382010-02-04 20:46:54 -0800940 } else {
San Mehatb1043402010-02-05 08:26:50 -0800941 return StorageResultCode.OperationFailedInternalError;
San Mehat207e5382010-02-04 20:46:54 -0800942 }
943 }
944 }
945
San Mehatb1043402010-02-05 08:26:50 -0800946 private boolean doGetVolumeShared(String path, String method) {
947 String cmd = String.format("volume shared %s %s", path, method);
Kenny Roota80ce062010-06-01 13:23:53 -0700948 ArrayList<String> rsp;
949
950 try {
951 rsp = mConnector.doCommand(cmd);
952 } catch (NativeDaemonConnectorException ex) {
953 Slog.e(TAG, "Failed to read response to volume shared " + path + " " + method);
954 return false;
955 }
San Mehatb1043402010-02-05 08:26:50 -0800956
957 for (String line : rsp) {
Kenny Roota80ce062010-06-01 13:23:53 -0700958 String[] tok = line.split(" ");
959 if (tok.length < 3) {
960 Slog.e(TAG, "Malformed response to volume shared " + path + " " + method + " command");
961 return false;
962 }
963
San Mehatb1043402010-02-05 08:26:50 -0800964 int code;
965 try {
966 code = Integer.parseInt(tok[0]);
967 } catch (NumberFormatException nfe) {
San Mehata5078592010-03-25 09:36:54 -0700968 Slog.e(TAG, String.format("Error parsing code %s", tok[0]));
San Mehatb1043402010-02-05 08:26:50 -0800969 return false;
970 }
971 if (code == VoldResponseCode.ShareEnabledResult) {
Kenny Roota80ce062010-06-01 13:23:53 -0700972 return "enabled".equals(tok[2]);
San Mehatb1043402010-02-05 08:26:50 -0800973 } else {
San Mehata5078592010-03-25 09:36:54 -0700974 Slog.e(TAG, String.format("Unexpected response code %d", code));
San Mehatb1043402010-02-05 08:26:50 -0800975 return false;
976 }
977 }
San Mehata5078592010-03-25 09:36:54 -0700978 Slog.e(TAG, "Got an empty response");
San Mehatb1043402010-02-05 08:26:50 -0800979 return false;
980 }
981
Mike Lockwoodecedfdc2011-06-08 15:11:59 -0700982 private void notifyShareAvailabilityChange(final boolean avail) {
San Mehat4270e1e2010-01-29 05:32:19 -0800983 synchronized (mListeners) {
Mike Lockwoodecedfdc2011-06-08 15:11:59 -0700984 mUmsAvailable = avail;
San Mehat4270e1e2010-01-29 05:32:19 -0800985 for (int i = mListeners.size() -1; i >= 0; i--) {
986 MountServiceBinderListener bl = mListeners.get(i);
987 try {
San Mehatb1043402010-02-05 08:26:50 -0800988 bl.mListener.onUsbMassStorageConnectionChanged(avail);
San Mehat4270e1e2010-01-29 05:32:19 -0800989 } catch (RemoteException rex) {
San Mehata5078592010-03-25 09:36:54 -0700990 Slog.e(TAG, "Listener dead");
San Mehat4270e1e2010-01-29 05:32:19 -0800991 mListeners.remove(i);
992 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700993 Slog.e(TAG, "Listener failed", ex);
San Mehat4270e1e2010-01-29 05:32:19 -0800994 }
995 }
996 }
997
San Mehat207e5382010-02-04 20:46:54 -0800998 if (mBooted == true) {
San Mehat6a965af22010-02-24 17:47:30 -0800999 sendUmsIntent(avail);
1000 } else {
1001 mSendUmsConnectedOnBoot = avail;
San Mehat4270e1e2010-01-29 05:32:19 -08001002 }
San Mehat2fe718a2010-03-11 12:01:49 -08001003
1004 final String path = Environment.getExternalStorageDirectory().getPath();
1005 if (avail == false && getVolumeState(path).equals(Environment.MEDIA_SHARED)) {
1006 /*
1007 * USB mass storage disconnected while enabled
1008 */
1009 new Thread() {
Jason parks5af0b912010-11-29 09:05:25 -06001010 @Override
San Mehat2fe718a2010-03-11 12:01:49 -08001011 public void run() {
1012 try {
1013 int rc;
San Mehata5078592010-03-25 09:36:54 -07001014 Slog.w(TAG, "Disabling UMS after cable disconnect");
San Mehat2fe718a2010-03-11 12:01:49 -08001015 doShareUnshareVolume(path, "ums", false);
1016 if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {
San Mehata5078592010-03-25 09:36:54 -07001017 Slog.e(TAG, String.format(
San Mehat2fe718a2010-03-11 12:01:49 -08001018 "Failed to remount {%s} on UMS enabled-disconnect (%d)",
1019 path, rc));
1020 }
1021 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -07001022 Slog.w(TAG, "Failed to mount media on UMS enabled-disconnect", ex);
San Mehat2fe718a2010-03-11 12:01:49 -08001023 }
1024 }
1025 }.start();
1026 }
San Mehat4270e1e2010-01-29 05:32:19 -08001027 }
1028
Mike Lockwooda5250c92011-05-23 13:44:04 -04001029 private void sendStorageIntent(String action, String path) {
1030 Intent intent = new Intent(action, Uri.parse("file://" + path));
1031 // add StorageVolume extra
1032 intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, mVolumeMap.get(path));
1033 Slog.d(TAG, "sendStorageIntent " + intent);
1034 mContext.sendBroadcast(intent);
1035 }
1036
San Mehat6a965af22010-02-24 17:47:30 -08001037 private void sendUmsIntent(boolean c) {
1038 mContext.sendBroadcast(
1039 new Intent((c ? Intent.ACTION_UMS_CONNECTED : Intent.ACTION_UMS_DISCONNECTED)));
1040 }
1041
San Mehat207e5382010-02-04 20:46:54 -08001042 private void validatePermission(String perm) {
San Mehat4270e1e2010-01-29 05:32:19 -08001043 if (mContext.checkCallingOrSelfPermission(perm) != PackageManager.PERMISSION_GRANTED) {
1044 throw new SecurityException(String.format("Requires %s permission", perm));
1045 }
1046 }
1047
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001048 // Storage list XML tags
1049 private static final String TAG_STORAGE_LIST = "StorageList";
1050 private static final String TAG_STORAGE = "storage";
1051
1052 private void readStorageList(Resources resources) {
1053 int id = com.android.internal.R.xml.storage_list;
1054 XmlResourceParser parser = resources.getXml(id);
1055 AttributeSet attrs = Xml.asAttributeSet(parser);
1056
1057 try {
1058 XmlUtils.beginDocument(parser, TAG_STORAGE_LIST);
1059 while (true) {
1060 XmlUtils.nextElement(parser);
1061
1062 String element = parser.getName();
1063 if (element == null) break;
1064
1065 if (TAG_STORAGE.equals(element)) {
1066 TypedArray a = resources.obtainAttributes(attrs,
1067 com.android.internal.R.styleable.Storage);
1068
1069 CharSequence path = a.getText(
1070 com.android.internal.R.styleable.Storage_mountPoint);
1071 CharSequence description = a.getText(
1072 com.android.internal.R.styleable.Storage_storageDescription);
1073 boolean primary = a.getBoolean(
1074 com.android.internal.R.styleable.Storage_primary, false);
1075 boolean removable = a.getBoolean(
1076 com.android.internal.R.styleable.Storage_removable, false);
1077 boolean emulated = a.getBoolean(
1078 com.android.internal.R.styleable.Storage_emulated, false);
1079 int mtpReserve = a.getInt(
1080 com.android.internal.R.styleable.Storage_mtpReserve, 0);
Mike Lockwood8e8b2802011-06-07 08:03:33 -07001081 boolean allowMassStorage = a.getBoolean(
1082 com.android.internal.R.styleable.Storage_allowMassStorage, false);
Mike Lockwood7a59dd22011-07-11 09:18:03 -04001083 // resource parser does not support longs, so XML value is in megabytes
1084 long maxFileSize = a.getInt(
1085 com.android.internal.R.styleable.Storage_maxFileSize, 0) * 1024L * 1024L;
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001086
1087 Slog.d(TAG, "got storage path: " + path + " description: " + description +
1088 " primary: " + primary + " removable: " + removable +
Mike Lockwood8e8b2802011-06-07 08:03:33 -07001089 " emulated: " + emulated + " mtpReserve: " + mtpReserve +
Mike Lockwood7a59dd22011-07-11 09:18:03 -04001090 " allowMassStorage: " + allowMassStorage +
1091 " maxFileSize: " + maxFileSize);
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001092 if (path == null || description == null) {
1093 Slog.e(TAG, "path or description is null in readStorageList");
1094 } else {
Mike Lockwooda5250c92011-05-23 13:44:04 -04001095 String pathString = path.toString();
1096 StorageVolume volume = new StorageVolume(pathString,
Mike Lockwood8e8b2802011-06-07 08:03:33 -07001097 description.toString(), removable, emulated,
Mike Lockwood7a59dd22011-07-11 09:18:03 -04001098 mtpReserve, allowMassStorage, maxFileSize);
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001099 if (primary) {
1100 if (mPrimaryVolume == null) {
1101 mPrimaryVolume = volume;
1102 } else {
1103 Slog.e(TAG, "multiple primary volumes in storage list");
1104 }
1105 }
1106 if (mPrimaryVolume == volume) {
1107 // primay volume must be first
1108 mVolumes.add(0, volume);
1109 } else {
1110 mVolumes.add(volume);
1111 }
Mike Lockwooda5250c92011-05-23 13:44:04 -04001112 mVolumeMap.put(pathString, volume);
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001113 }
1114 a.recycle();
1115 }
1116 }
1117 } catch (XmlPullParserException e) {
1118 throw new RuntimeException(e);
1119 } catch (IOException e) {
1120 throw new RuntimeException(e);
1121 } finally {
Mike Lockwoodfbfe5552011-05-17 17:19:37 -04001122 // compute storage ID for each volume
1123 int length = mVolumes.size();
1124 for (int i = 0; i < length; i++) {
1125 mVolumes.get(i).setStorageId(i);
1126 }
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001127 parser.close();
1128 }
1129 }
1130
San Mehat4270e1e2010-01-29 05:32:19 -08001131 /**
San Mehat207e5382010-02-04 20:46:54 -08001132 * Constructs a new MountService instance
1133 *
1134 * @param context Binder context for this service
1135 */
1136 public MountService(Context context) {
1137 mContext = context;
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001138 Resources resources = context.getResources();
1139 readStorageList(resources);
San Mehat207e5382010-02-04 20:46:54 -08001140
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001141 if (mPrimaryVolume != null) {
1142 mExternalStoragePath = mPrimaryVolume.getPath();
1143 mEmulateExternalStorage = mPrimaryVolume.isEmulated();
1144 if (mEmulateExternalStorage) {
1145 Slog.d(TAG, "using emulated external storage");
1146 mVolumeStates.put(mExternalStoragePath, Environment.MEDIA_MOUNTED);
1147 }
Mike Lockwood03559752010-07-19 18:25:03 -04001148 }
1149
San Mehat207e5382010-02-04 20:46:54 -08001150 // XXX: This will go away soon in favor of IMountServiceObserver
1151 mPms = (PackageManagerService) ServiceManager.getService("package");
1152
Mike Lockwoodecedfdc2011-06-08 15:11:59 -07001153 IntentFilter filter = new IntentFilter();
1154 filter.addAction(Intent.ACTION_BOOT_COMPLETED);
1155 // don't bother monitoring USB if mass storage is not supported on our primary volume
1156 if (mPrimaryVolume != null && mPrimaryVolume.allowMassStorage()) {
1157 filter.addAction(UsbManager.ACTION_USB_STATE);
1158 }
1159 mContext.registerReceiver(mBroadcastReceiver, filter, null, null);
San Mehat207e5382010-02-04 20:46:54 -08001160
Daniel Sandler5f27ef42010-03-16 15:42:02 -04001161 mHandlerThread = new HandlerThread("MountService");
1162 mHandlerThread.start();
1163 mHandler = new MountServiceHandler(mHandlerThread.getLooper());
1164
Kenny Roota02b8b02010-08-05 16:14:17 -07001165 // Add OBB Action Handler to MountService thread.
1166 mObbActionHandler = new ObbActionHandler(mHandlerThread.getLooper());
1167
Marco Nelissenc34ebce2010-02-18 13:39:41 -08001168 /*
1169 * Vold does not run in the simulator, so pretend the connector thread
1170 * ran and did its thing.
1171 */
1172 if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
1173 mReady = true;
1174 mUmsEnabling = true;
1175 return;
1176 }
1177
Kenny Root305bcbf2010-09-03 07:56:38 -07001178 /*
1179 * Create the connection to vold with a maximum queue of twice the
1180 * amount of containers we'd ever expect to have. This keeps an
1181 * "asec list" from blocking a thread repeatedly.
1182 */
Kenny Rootcf0b38c2011-03-22 14:17:59 -07001183 mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG);
San Mehat207e5382010-02-04 20:46:54 -08001184 mReady = false;
Kenny Root305bcbf2010-09-03 07:56:38 -07001185 Thread thread = new Thread(mConnector, VOLD_TAG);
San Mehat207e5382010-02-04 20:46:54 -08001186 thread.start();
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -07001187
Kenny Root07714d42011-08-17 17:49:28 -07001188 // Add ourself to the Watchdog monitors if enabled.
1189 if (WATCHDOG_ENABLE) {
1190 Watchdog.getInstance().addMonitor(this);
1191 }
San Mehat207e5382010-02-04 20:46:54 -08001192 }
1193
1194 /**
San Mehat4270e1e2010-01-29 05:32:19 -08001195 * Exposed API calls below here
1196 */
1197
1198 public void registerListener(IMountServiceListener listener) {
1199 synchronized (mListeners) {
1200 MountServiceBinderListener bl = new MountServiceBinderListener(listener);
1201 try {
1202 listener.asBinder().linkToDeath(bl, 0);
1203 mListeners.add(bl);
1204 } catch (RemoteException rex) {
San Mehata5078592010-03-25 09:36:54 -07001205 Slog.e(TAG, "Failed to link to listener death");
San Mehat4270e1e2010-01-29 05:32:19 -08001206 }
1207 }
1208 }
1209
1210 public void unregisterListener(IMountServiceListener listener) {
1211 synchronized (mListeners) {
1212 for(MountServiceBinderListener bl : mListeners) {
1213 if (bl.mListener == listener) {
1214 mListeners.remove(mListeners.indexOf(bl));
1215 return;
1216 }
1217 }
1218 }
1219 }
1220
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -08001221 public void shutdown(final IMountShutdownObserver observer) {
San Mehat4270e1e2010-01-29 05:32:19 -08001222 validatePermission(android.Manifest.permission.SHUTDOWN);
1223
San Mehata5078592010-03-25 09:36:54 -07001224 Slog.i(TAG, "Shutting down");
Mike Lockwood7fa24aa2011-03-23 14:52:34 -04001225 synchronized (mVolumeStates) {
1226 for (String path : mVolumeStates.keySet()) {
1227 String state = mVolumeStates.get(path);
San Mehat4270e1e2010-01-29 05:32:19 -08001228
Mike Lockwood7fa24aa2011-03-23 14:52:34 -04001229 if (state.equals(Environment.MEDIA_SHARED)) {
1230 /*
1231 * If the media is currently shared, unshare it.
1232 * XXX: This is still dangerous!. We should not
1233 * be rebooting at *all* if UMS is enabled, since
1234 * the UMS host could have dirty FAT cache entries
1235 * yet to flush.
1236 */
1237 setUsbMassStorageEnabled(false);
1238 } else if (state.equals(Environment.MEDIA_CHECKING)) {
1239 /*
1240 * If the media is being checked, then we need to wait for
1241 * it to complete before being able to proceed.
1242 */
1243 // XXX: @hackbod - Should we disable the ANR timer here?
1244 int retries = 30;
1245 while (state.equals(Environment.MEDIA_CHECKING) && (retries-- >=0)) {
1246 try {
1247 Thread.sleep(1000);
1248 } catch (InterruptedException iex) {
1249 Slog.e(TAG, "Interrupted while waiting for media", iex);
1250 break;
1251 }
1252 state = Environment.getExternalStorageState();
1253 }
1254 if (retries == 0) {
1255 Slog.e(TAG, "Timed out waiting for media to check");
1256 }
San Mehat91c77612010-01-07 10:39:41 -08001257 }
San Mehat91c77612010-01-07 10:39:41 -08001258
Mike Lockwood7fa24aa2011-03-23 14:52:34 -04001259 if (state.equals(Environment.MEDIA_MOUNTED)) {
1260 // Post a unmount message.
1261 ShutdownCallBack ucb = new ShutdownCallBack(path, observer);
1262 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
1263 } else if (observer != null) {
1264 /*
1265 * Observer is waiting for onShutDownComplete when we are done.
1266 * Since nothing will be done send notification directly so shutdown
1267 * sequence can continue.
1268 */
1269 try {
1270 observer.onShutDownComplete(StorageResultCode.OperationSucceeded);
1271 } catch (RemoteException e) {
1272 Slog.w(TAG, "RemoteException when shutting down");
1273 }
1274 }
Johan Alfven5d0db4d2010-11-09 10:32:25 +01001275 }
San Mehat4270e1e2010-01-29 05:32:19 -08001276 }
1277 }
1278
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001279 private boolean getUmsEnabling() {
1280 synchronized (mListeners) {
1281 return mUmsEnabling;
1282 }
1283 }
1284
1285 private void setUmsEnabling(boolean enable) {
1286 synchronized (mListeners) {
Tony Wufc711252010-08-09 16:49:19 +08001287 mUmsEnabling = enable;
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001288 }
1289 }
1290
San Mehatb1043402010-02-05 08:26:50 -08001291 public boolean isUsbMassStorageConnected() {
San Mehat207e5382010-02-04 20:46:54 -08001292 waitForReady();
San Mehat91c77612010-01-07 10:39:41 -08001293
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001294 if (getUmsEnabling()) {
San Mehatb1043402010-02-05 08:26:50 -08001295 return true;
San Mehat7fd0fee2009-12-17 07:12:23 -08001296 }
Mike Lockwoodecedfdc2011-06-08 15:11:59 -07001297 synchronized (mListeners) {
1298 return mUmsAvailable;
1299 }
San Mehatb1043402010-02-05 08:26:50 -08001300 }
1301
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001302 public void setUsbMassStorageEnabled(boolean enable) {
San Mehatb1043402010-02-05 08:26:50 -08001303 waitForReady();
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001304 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
San Mehatb1043402010-02-05 08:26:50 -08001305
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001306 // TODO: Add support for multiple share methods
1307
1308 /*
1309 * If the volume is mounted and we're enabling then unmount it
1310 */
1311 String path = Environment.getExternalStorageDirectory().getPath();
1312 String vs = getVolumeState(path);
1313 String method = "ums";
1314 if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
1315 // Override for isUsbMassStorageEnabled()
1316 setUmsEnabling(enable);
1317 UmsEnableCallBack umscb = new UmsEnableCallBack(path, method, true);
1318 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, umscb));
1319 // Clear override
1320 setUmsEnabling(false);
1321 }
1322 /*
1323 * If we disabled UMS then mount the volume
1324 */
1325 if (!enable) {
1326 doShareUnshareVolume(path, method, enable);
1327 if (doMountVolume(path) != StorageResultCode.OperationSucceeded) {
San Mehata5078592010-03-25 09:36:54 -07001328 Slog.e(TAG, "Failed to remount " + path +
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001329 " after disabling share method " + method);
1330 /*
1331 * Even though the mount failed, the unshare didn't so don't indicate an error.
1332 * The mountVolume() call will have set the storage state and sent the necessary
1333 * broadcasts.
1334 */
1335 }
1336 }
San Mehatb1043402010-02-05 08:26:50 -08001337 }
1338
1339 public boolean isUsbMassStorageEnabled() {
1340 waitForReady();
1341 return doGetVolumeShared(Environment.getExternalStorageDirectory().getPath(), "ums");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001342 }
Jason parks9ed98bc2011-01-17 09:58:35 -06001343
San Mehat7fd0fee2009-12-17 07:12:23 -08001344 /**
1345 * @return state of the volume at the specified mount point
1346 */
San Mehat4270e1e2010-01-29 05:32:19 -08001347 public String getVolumeState(String mountPoint) {
Mike Lockwood7fa24aa2011-03-23 14:52:34 -04001348 synchronized (mVolumeStates) {
1349 String state = mVolumeStates.get(mountPoint);
1350 if (state == null) {
1351 Slog.w(TAG, "getVolumeState(" + mountPoint + "): Unknown volume");
Ken Sumrall18db5c52011-07-14 11:35:06 -07001352 if (SystemProperties.get("vold.encrypt_progress").length() != 0) {
1353 state = Environment.MEDIA_REMOVED;
1354 } else {
1355 throw new IllegalArgumentException();
1356 }
Mike Lockwood7fa24aa2011-03-23 14:52:34 -04001357 }
San Mehat7fd0fee2009-12-17 07:12:23 -08001358
Mike Lockwood7fa24aa2011-03-23 14:52:34 -04001359 return state;
1360 }
San Mehat7fd0fee2009-12-17 07:12:23 -08001361 }
1362
Kenny Roote1ff2142010-10-12 11:20:01 -07001363 public boolean isExternalStorageEmulated() {
1364 return mEmulateExternalStorage;
1365 }
1366
San Mehat4270e1e2010-01-29 05:32:19 -08001367 public int mountVolume(String path) {
1368 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
San Mehat4270e1e2010-01-29 05:32:19 -08001369
San Mehat207e5382010-02-04 20:46:54 -08001370 waitForReady();
1371 return doMountVolume(path);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001372 }
1373
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -08001374 public void unmountVolume(String path, boolean force) {
San Mehat4270e1e2010-01-29 05:32:19 -08001375 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
San Mehat207e5382010-02-04 20:46:54 -08001376 waitForReady();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001377
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -08001378 String volState = getVolumeState(path);
San Mehata5078592010-03-25 09:36:54 -07001379 if (DEBUG_UNMOUNT) Slog.i(TAG, "Unmounting " + path + " force = " + force);
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -08001380 if (Environment.MEDIA_UNMOUNTED.equals(volState) ||
1381 Environment.MEDIA_REMOVED.equals(volState) ||
1382 Environment.MEDIA_SHARED.equals(volState) ||
1383 Environment.MEDIA_UNMOUNTABLE.equals(volState)) {
1384 // Media already unmounted or cannot be unmounted.
1385 // TODO return valid return code when adding observer call back.
1386 return;
1387 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -08001388 UnmountCallBack ucb = new UnmountCallBack(path, force);
1389 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001390 }
1391
San Mehat4270e1e2010-01-29 05:32:19 -08001392 public int formatVolume(String path) {
1393 validatePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
San Mehat207e5382010-02-04 20:46:54 -08001394 waitForReady();
San Mehat5b77dab2010-01-26 13:28:50 -08001395
San Mehat207e5382010-02-04 20:46:54 -08001396 return doFormatVolume(path);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001397 }
1398
Mike Lockwoodecedfdc2011-06-08 15:11:59 -07001399 public int[] getStorageUsers(String path) {
San Mehatc1b4ce92010-02-16 17:13:03 -08001400 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1401 waitForReady();
1402 try {
1403 String[] r = mConnector.doListCommand(
1404 String.format("storage users %s", path),
1405 VoldResponseCode.StorageUsersListResult);
1406 // FMT: <pid> <process name>
1407 int[] data = new int[r.length];
1408 for (int i = 0; i < r.length; i++) {
1409 String []tok = r[i].split(" ");
1410 try {
1411 data[i] = Integer.parseInt(tok[0]);
1412 } catch (NumberFormatException nfe) {
San Mehata5078592010-03-25 09:36:54 -07001413 Slog.e(TAG, String.format("Error parsing pid %s", tok[0]));
San Mehatc1b4ce92010-02-16 17:13:03 -08001414 return new int[0];
1415 }
1416 }
1417 return data;
1418 } catch (NativeDaemonConnectorException e) {
San Mehata5078592010-03-25 09:36:54 -07001419 Slog.e(TAG, "Failed to retrieve storage users list", e);
San Mehatc1b4ce92010-02-16 17:13:03 -08001420 return new int[0];
1421 }
1422 }
1423
San Mehatb1043402010-02-05 08:26:50 -08001424 private void warnOnNotMounted() {
1425 if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
San Mehata5078592010-03-25 09:36:54 -07001426 Slog.w(TAG, "getSecureContainerList() called when storage not mounted");
San Mehatb1043402010-02-05 08:26:50 -08001427 }
1428 }
1429
San Mehat4270e1e2010-01-29 05:32:19 -08001430 public String[] getSecureContainerList() {
1431 validatePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat207e5382010-02-04 20:46:54 -08001432 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001433 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001434
San Mehat4270e1e2010-01-29 05:32:19 -08001435 try {
1436 return mConnector.doListCommand("asec list", VoldResponseCode.AsecListResult);
1437 } catch (NativeDaemonConnectorException e) {
1438 return new String[0];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001439 }
1440 }
San Mehat36972292010-01-06 11:06:32 -08001441
San Mehat4270e1e2010-01-29 05:32:19 -08001442 public int createSecureContainer(String id, int sizeMb, String fstype,
1443 String key, int ownerUid) {
1444 validatePermission(android.Manifest.permission.ASEC_CREATE);
San Mehat207e5382010-02-04 20:46:54 -08001445 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001446 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001447
San Mehatb1043402010-02-05 08:26:50 -08001448 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001449 String cmd = String.format("asec create %s %d %s %s %d", id, sizeMb, fstype, key, ownerUid);
1450 try {
1451 mConnector.doCommand(cmd);
1452 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001453 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001454 }
San Mehata181b212010-02-11 06:50:20 -08001455
1456 if (rc == StorageResultCode.OperationSucceeded) {
1457 synchronized (mAsecMountSet) {
1458 mAsecMountSet.add(id);
1459 }
1460 }
San Mehat4270e1e2010-01-29 05:32:19 -08001461 return rc;
San Mehat36972292010-01-06 11:06:32 -08001462 }
1463
San Mehat4270e1e2010-01-29 05:32:19 -08001464 public int finalizeSecureContainer(String id) {
1465 validatePermission(android.Manifest.permission.ASEC_CREATE);
San Mehatb1043402010-02-05 08:26:50 -08001466 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001467
San Mehatb1043402010-02-05 08:26:50 -08001468 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001469 try {
1470 mConnector.doCommand(String.format("asec finalize %s", id));
San Mehata181b212010-02-11 06:50:20 -08001471 /*
1472 * Finalization does a remount, so no need
1473 * to update mAsecMountSet
1474 */
San Mehat4270e1e2010-01-29 05:32:19 -08001475 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001476 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001477 }
San Mehat4270e1e2010-01-29 05:32:19 -08001478 return rc;
San Mehat36972292010-01-06 11:06:32 -08001479 }
1480
San Mehatd9709982010-02-18 11:43:03 -08001481 public int destroySecureContainer(String id, boolean force) {
San Mehat4270e1e2010-01-29 05:32:19 -08001482 validatePermission(android.Manifest.permission.ASEC_DESTROY);
San Mehat207e5382010-02-04 20:46:54 -08001483 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001484 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001485
Kenny Rootaa485402010-09-14 14:49:41 -07001486 /*
1487 * Force a GC to make sure AssetManagers in other threads of the
1488 * system_server are cleaned up. We have to do this since AssetManager
1489 * instances are kept as a WeakReference and it's possible we have files
1490 * open on the external storage.
1491 */
1492 Runtime.getRuntime().gc();
1493
San Mehatb1043402010-02-05 08:26:50 -08001494 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001495 try {
San Mehatd9709982010-02-18 11:43:03 -08001496 mConnector.doCommand(String.format("asec destroy %s%s", id, (force ? " force" : "")));
San Mehat4270e1e2010-01-29 05:32:19 -08001497 } catch (NativeDaemonConnectorException e) {
San Mehatd9709982010-02-18 11:43:03 -08001498 int code = e.getCode();
1499 if (code == VoldResponseCode.OpFailedStorageBusy) {
1500 rc = StorageResultCode.OperationFailedStorageBusy;
1501 } else {
1502 rc = StorageResultCode.OperationFailedInternalError;
1503 }
San Mehat02735bc2010-01-26 15:18:08 -08001504 }
San Mehata181b212010-02-11 06:50:20 -08001505
1506 if (rc == StorageResultCode.OperationSucceeded) {
1507 synchronized (mAsecMountSet) {
1508 if (mAsecMountSet.contains(id)) {
1509 mAsecMountSet.remove(id);
1510 }
1511 }
1512 }
1513
San Mehat4270e1e2010-01-29 05:32:19 -08001514 return rc;
San Mehat36972292010-01-06 11:06:32 -08001515 }
Jason parks9ed98bc2011-01-17 09:58:35 -06001516
San Mehat4270e1e2010-01-29 05:32:19 -08001517 public int mountSecureContainer(String id, String key, int ownerUid) {
1518 validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
San Mehat207e5382010-02-04 20:46:54 -08001519 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001520 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001521
San Mehata181b212010-02-11 06:50:20 -08001522 synchronized (mAsecMountSet) {
1523 if (mAsecMountSet.contains(id)) {
1524 return StorageResultCode.OperationFailedStorageMounted;
1525 }
1526 }
1527
San Mehatb1043402010-02-05 08:26:50 -08001528 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001529 String cmd = String.format("asec mount %s %s %d", id, key, ownerUid);
1530 try {
1531 mConnector.doCommand(cmd);
1532 } catch (NativeDaemonConnectorException e) {
Kenny Rootf0304622010-03-19 19:20:42 -07001533 int code = e.getCode();
1534 if (code != VoldResponseCode.OpFailedStorageBusy) {
1535 rc = StorageResultCode.OperationFailedInternalError;
1536 }
San Mehat02735bc2010-01-26 15:18:08 -08001537 }
San Mehat6cdd9c02010-02-09 14:45:20 -08001538
1539 if (rc == StorageResultCode.OperationSucceeded) {
1540 synchronized (mAsecMountSet) {
1541 mAsecMountSet.add(id);
1542 }
1543 }
San Mehat4270e1e2010-01-29 05:32:19 -08001544 return rc;
San Mehat36972292010-01-06 11:06:32 -08001545 }
1546
San Mehatd9709982010-02-18 11:43:03 -08001547 public int unmountSecureContainer(String id, boolean force) {
San Mehat4270e1e2010-01-29 05:32:19 -08001548 validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
San Mehat207e5382010-02-04 20:46:54 -08001549 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001550 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001551
San Mehat6cdd9c02010-02-09 14:45:20 -08001552 synchronized (mAsecMountSet) {
1553 if (!mAsecMountSet.contains(id)) {
San Mehata181b212010-02-11 06:50:20 -08001554 return StorageResultCode.OperationFailedStorageNotMounted;
San Mehat6cdd9c02010-02-09 14:45:20 -08001555 }
1556 }
1557
Kenny Rootaa485402010-09-14 14:49:41 -07001558 /*
1559 * Force a GC to make sure AssetManagers in other threads of the
1560 * system_server are cleaned up. We have to do this since AssetManager
1561 * instances are kept as a WeakReference and it's possible we have files
1562 * open on the external storage.
1563 */
1564 Runtime.getRuntime().gc();
1565
San Mehatb1043402010-02-05 08:26:50 -08001566 int rc = StorageResultCode.OperationSucceeded;
San Mehatd9709982010-02-18 11:43:03 -08001567 String cmd = String.format("asec unmount %s%s", id, (force ? " force" : ""));
San Mehat4270e1e2010-01-29 05:32:19 -08001568 try {
1569 mConnector.doCommand(cmd);
1570 } catch (NativeDaemonConnectorException e) {
San Mehatd9709982010-02-18 11:43:03 -08001571 int code = e.getCode();
1572 if (code == VoldResponseCode.OpFailedStorageBusy) {
1573 rc = StorageResultCode.OperationFailedStorageBusy;
1574 } else {
1575 rc = StorageResultCode.OperationFailedInternalError;
1576 }
San Mehat02735bc2010-01-26 15:18:08 -08001577 }
San Mehat6cdd9c02010-02-09 14:45:20 -08001578
1579 if (rc == StorageResultCode.OperationSucceeded) {
1580 synchronized (mAsecMountSet) {
1581 mAsecMountSet.remove(id);
1582 }
1583 }
San Mehat4270e1e2010-01-29 05:32:19 -08001584 return rc;
San Mehat9dba7092010-01-18 06:47:41 -08001585 }
1586
San Mehat6cdd9c02010-02-09 14:45:20 -08001587 public boolean isSecureContainerMounted(String id) {
1588 validatePermission(android.Manifest.permission.ASEC_ACCESS);
1589 waitForReady();
1590 warnOnNotMounted();
1591
1592 synchronized (mAsecMountSet) {
1593 return mAsecMountSet.contains(id);
1594 }
1595 }
1596
San Mehat4270e1e2010-01-29 05:32:19 -08001597 public int renameSecureContainer(String oldId, String newId) {
1598 validatePermission(android.Manifest.permission.ASEC_RENAME);
San Mehat207e5382010-02-04 20:46:54 -08001599 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001600 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001601
San Mehata181b212010-02-11 06:50:20 -08001602 synchronized (mAsecMountSet) {
San Mehat85451ee2010-02-24 08:54:18 -08001603 /*
Jason parks9ed98bc2011-01-17 09:58:35 -06001604 * Because a mounted container has active internal state which cannot be
San Mehat85451ee2010-02-24 08:54:18 -08001605 * changed while active, we must ensure both ids are not currently mounted.
1606 */
1607 if (mAsecMountSet.contains(oldId) || mAsecMountSet.contains(newId)) {
San Mehata181b212010-02-11 06:50:20 -08001608 return StorageResultCode.OperationFailedStorageMounted;
1609 }
1610 }
1611
San Mehatb1043402010-02-05 08:26:50 -08001612 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001613 String cmd = String.format("asec rename %s %s", oldId, newId);
1614 try {
1615 mConnector.doCommand(cmd);
1616 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001617 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001618 }
San Mehata181b212010-02-11 06:50:20 -08001619
San Mehat4270e1e2010-01-29 05:32:19 -08001620 return rc;
San Mehat45f61042010-01-23 08:12:43 -08001621 }
1622
San Mehat4270e1e2010-01-29 05:32:19 -08001623 public String getSecureContainerPath(String id) {
1624 validatePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat207e5382010-02-04 20:46:54 -08001625 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001626 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001627
San Mehat2d66cef2010-03-23 11:12:52 -07001628 try {
1629 ArrayList<String> rsp = mConnector.doCommand(String.format("asec path %s", id));
1630 String []tok = rsp.get(0).split(" ");
San Mehat22dd86e2010-01-12 12:21:18 -08001631 int code = Integer.parseInt(tok[0]);
San Mehat2d66cef2010-03-23 11:12:52 -07001632 if (code != VoldResponseCode.AsecPathResult) {
1633 throw new IllegalStateException(String.format("Unexpected response code %d", code));
1634 }
1635 return tok[1];
1636 } catch (NativeDaemonConnectorException e) {
1637 int code = e.getCode();
1638 if (code == VoldResponseCode.OpFailedStorageNotFound) {
Fredrik Helmera20c8ef2011-02-09 16:16:10 +01001639 Slog.i(TAG, String.format("Container '%s' not found", id));
1640 return null;
San Mehat22dd86e2010-01-12 12:21:18 -08001641 } else {
San Mehat2d66cef2010-03-23 11:12:52 -07001642 throw new IllegalStateException(String.format("Unexpected response code %d", code));
San Mehat22dd86e2010-01-12 12:21:18 -08001643 }
1644 }
San Mehat22dd86e2010-01-12 12:21:18 -08001645 }
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -07001646
Dianne Hackborn292f8bc2011-06-27 16:27:41 -07001647 public String getSecureContainerFilesystemPath(String id) {
1648 validatePermission(android.Manifest.permission.ASEC_ACCESS);
1649 waitForReady();
1650 warnOnNotMounted();
1651
1652 try {
1653 ArrayList<String> rsp = mConnector.doCommand(String.format("asec fspath %s", id));
1654 String []tok = rsp.get(0).split(" ");
1655 int code = Integer.parseInt(tok[0]);
1656 if (code != VoldResponseCode.AsecPathResult) {
1657 throw new IllegalStateException(String.format("Unexpected response code %d", code));
1658 }
1659 return tok[1];
1660 } catch (NativeDaemonConnectorException e) {
1661 int code = e.getCode();
1662 if (code == VoldResponseCode.OpFailedStorageNotFound) {
1663 Slog.i(TAG, String.format("Container '%s' not found", id));
1664 return null;
1665 } else {
1666 throw new IllegalStateException(String.format("Unexpected response code %d", code));
1667 }
1668 }
1669 }
1670
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -07001671 public void finishMediaUpdate() {
1672 mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
1673 }
Kenny Root02c87302010-07-01 08:10:18 -07001674
Kenny Roota02b8b02010-08-05 16:14:17 -07001675 private boolean isUidOwnerOfPackageOrSystem(String packageName, int callerUid) {
1676 if (callerUid == android.os.Process.SYSTEM_UID) {
1677 return true;
1678 }
1679
Kenny Root02c87302010-07-01 08:10:18 -07001680 if (packageName == null) {
1681 return false;
1682 }
1683
1684 final int packageUid = mPms.getPackageUid(packageName);
1685
1686 if (DEBUG_OBB) {
1687 Slog.d(TAG, "packageName = " + packageName + ", packageUid = " +
1688 packageUid + ", callerUid = " + callerUid);
1689 }
1690
1691 return callerUid == packageUid;
1692 }
1693
1694 public String getMountedObbPath(String filename) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07001695 if (filename == null) {
1696 throw new IllegalArgumentException("filename cannot be null");
1697 }
1698
Kenny Root02c87302010-07-01 08:10:18 -07001699 waitForReady();
1700 warnOnNotMounted();
1701
Kenny Root02c87302010-07-01 08:10:18 -07001702 try {
1703 ArrayList<String> rsp = mConnector.doCommand(String.format("obb path %s", filename));
1704 String []tok = rsp.get(0).split(" ");
1705 int code = Integer.parseInt(tok[0]);
1706 if (code != VoldResponseCode.AsecPathResult) {
1707 throw new IllegalStateException(String.format("Unexpected response code %d", code));
1708 }
1709 return tok[1];
1710 } catch (NativeDaemonConnectorException e) {
1711 int code = e.getCode();
1712 if (code == VoldResponseCode.OpFailedStorageNotFound) {
Kenny Roota02b8b02010-08-05 16:14:17 -07001713 return null;
Kenny Root02c87302010-07-01 08:10:18 -07001714 } else {
1715 throw new IllegalStateException(String.format("Unexpected response code %d", code));
1716 }
1717 }
1718 }
1719
1720 public boolean isObbMounted(String filename) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07001721 if (filename == null) {
1722 throw new IllegalArgumentException("filename cannot be null");
Kenny Root02c87302010-07-01 08:10:18 -07001723 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07001724
1725 synchronized (mObbMounts) {
1726 return mObbPathToStateMap.containsKey(filename);
1727 }
Kenny Root02c87302010-07-01 08:10:18 -07001728 }
1729
Kenny Rootaf9d6672010-10-08 09:21:39 -07001730 public void mountObb(String filename, String key, IObbActionListener token, int nonce)
Kenny Root735de3b2010-09-30 14:11:39 -07001731 throws RemoteException {
Kenny Rootf1121dc2010-09-29 07:30:53 -07001732 if (filename == null) {
1733 throw new IllegalArgumentException("filename cannot be null");
Kenny Rootaf9d6672010-10-08 09:21:39 -07001734 }
1735
1736 if (token == null) {
Kenny Rootf1121dc2010-09-29 07:30:53 -07001737 throw new IllegalArgumentException("token cannot be null");
1738 }
1739
Kenny Rootaf9d6672010-10-08 09:21:39 -07001740 final int callerUid = Binder.getCallingUid();
1741 final ObbState obbState = new ObbState(filename, callerUid, token, nonce);
1742 final ObbAction action = new MountObbAction(obbState, key);
Kenny Roota02b8b02010-08-05 16:14:17 -07001743 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
1744
1745 if (DEBUG_OBB)
1746 Slog.i(TAG, "Send to OBB handler: " + action.toString());
Kenny Root02c87302010-07-01 08:10:18 -07001747 }
1748
Kenny Rootaf9d6672010-10-08 09:21:39 -07001749 public void unmountObb(String filename, boolean force, IObbActionListener token, int nonce)
1750 throws RemoteException {
Kenny Rootf1121dc2010-09-29 07:30:53 -07001751 if (filename == null) {
1752 throw new IllegalArgumentException("filename cannot be null");
Kenny Rootf1121dc2010-09-29 07:30:53 -07001753 }
1754
Kenny Rootaf9d6672010-10-08 09:21:39 -07001755 final int callerUid = Binder.getCallingUid();
1756 final ObbState obbState = new ObbState(filename, callerUid, token, nonce);
1757 final ObbAction action = new UnmountObbAction(obbState, force);
Kenny Roota02b8b02010-08-05 16:14:17 -07001758 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
Kenny Root02c87302010-07-01 08:10:18 -07001759
Kenny Roota02b8b02010-08-05 16:14:17 -07001760 if (DEBUG_OBB)
1761 Slog.i(TAG, "Send to OBB handler: " + action.toString());
1762 }
1763
Ben Komalo444eca22011-09-01 15:17:44 -07001764 @Override
1765 public int getEncryptionState() {
1766 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
1767 "no permission to access the crypt keeper");
1768
1769 waitForReady();
1770
1771 try {
1772 ArrayList<String> rsp = mConnector.doCommand("cryptfs cryptocomplete");
1773 String[] tokens = rsp.get(0).split(" ");
1774
1775 if (tokens == null || tokens.length != 2) {
1776 // Unexpected.
1777 Slog.w(TAG, "Unexpected result from cryptfs cryptocomplete");
1778 return ENCRYPTION_STATE_ERROR_UNKNOWN;
1779 }
1780
1781 return Integer.parseInt(tokens[1]);
1782
1783 } catch (NumberFormatException e) {
1784 // Bad result - unexpected.
1785 Slog.w(TAG, "Unable to parse result from cryptfs cryptocomplete");
1786 return ENCRYPTION_STATE_ERROR_UNKNOWN;
1787 } catch (NativeDaemonConnectorException e) {
1788 // Something bad happened.
1789 Slog.w(TAG, "Error in communicating with cryptfs in validating");
1790 return ENCRYPTION_STATE_ERROR_UNKNOWN;
1791 }
1792 }
1793
1794 @Override
Jason parks5af0b912010-11-29 09:05:25 -06001795 public int decryptStorage(String password) {
Jason parksf7b3cd42011-01-27 09:28:25 -06001796 if (TextUtils.isEmpty(password)) {
1797 throw new IllegalArgumentException("password cannot be empty");
Jason parks5af0b912010-11-29 09:05:25 -06001798 }
1799
Jason parks8888c592011-01-20 22:46:41 -06001800 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
1801 "no permission to access the crypt keeper");
Jason parks5af0b912010-11-29 09:05:25 -06001802
1803 waitForReady();
1804
1805 if (DEBUG_EVENTS) {
1806 Slog.i(TAG, "decrypting storage...");
1807 }
1808
1809 try {
Jason parks9ed98bc2011-01-17 09:58:35 -06001810 ArrayList<String> rsp = mConnector.doCommand("cryptfs checkpw " + password);
Jason parksf7b3cd42011-01-27 09:28:25 -06001811 String[] tokens = rsp.get(0).split(" ");
Jason parks9ed98bc2011-01-17 09:58:35 -06001812
Jason parksf7b3cd42011-01-27 09:28:25 -06001813 if (tokens == null || tokens.length != 2) {
Jason parks9ed98bc2011-01-17 09:58:35 -06001814 return -1;
1815 }
1816
Jason parksf7b3cd42011-01-27 09:28:25 -06001817 int code = Integer.parseInt(tokens[1]);
Jason parks9ed98bc2011-01-17 09:58:35 -06001818
1819 if (code == 0) {
1820 // Decrypt was successful. Post a delayed message before restarting in order
1821 // to let the UI to clear itself
1822 mHandler.postDelayed(new Runnable() {
1823 public void run() {
1824 mConnector.doCommand(String.format("cryptfs restart"));
1825 }
Jason parksf7b3cd42011-01-27 09:28:25 -06001826 }, 1000); // 1 second
Jason parks9ed98bc2011-01-17 09:58:35 -06001827 }
1828
1829 return code;
Jason parks5af0b912010-11-29 09:05:25 -06001830 } catch (NativeDaemonConnectorException e) {
1831 // Decryption failed
1832 return e.getCode();
1833 }
Jason parks5af0b912010-11-29 09:05:25 -06001834 }
1835
Jason parks56aa5322011-01-07 09:01:15 -06001836 public int encryptStorage(String password) {
Jason parksf7b3cd42011-01-27 09:28:25 -06001837 if (TextUtils.isEmpty(password)) {
1838 throw new IllegalArgumentException("password cannot be empty");
Jason parks56aa5322011-01-07 09:01:15 -06001839 }
1840
Jason parks8888c592011-01-20 22:46:41 -06001841 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
1842 "no permission to access the crypt keeper");
Jason parks56aa5322011-01-07 09:01:15 -06001843
1844 waitForReady();
1845
1846 if (DEBUG_EVENTS) {
Jason parks8888c592011-01-20 22:46:41 -06001847 Slog.i(TAG, "encrypting storage...");
Jason parks56aa5322011-01-07 09:01:15 -06001848 }
1849
1850 try {
Jason parks9ed98bc2011-01-17 09:58:35 -06001851 mConnector.doCommand(String.format("cryptfs enablecrypto inplace %s", password));
Jason parks56aa5322011-01-07 09:01:15 -06001852 } catch (NativeDaemonConnectorException e) {
1853 // Encryption failed
1854 return e.getCode();
1855 }
1856
1857 return 0;
1858 }
1859
Jason parksf7b3cd42011-01-27 09:28:25 -06001860 public int changeEncryptionPassword(String password) {
1861 if (TextUtils.isEmpty(password)) {
1862 throw new IllegalArgumentException("password cannot be empty");
1863 }
1864
1865 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
1866 "no permission to access the crypt keeper");
1867
1868 waitForReady();
1869
1870 if (DEBUG_EVENTS) {
1871 Slog.i(TAG, "changing encryption password...");
1872 }
1873
1874 try {
1875 ArrayList<String> response = mConnector.doCommand("cryptfs changepw " + password);
1876
1877 String[] tokens = response.get(0).split(" ");
1878
1879 if (tokens == null || tokens.length != 2) {
1880 return -1;
1881 }
1882
1883 return Integer.parseInt(tokens[1]);
1884 } catch (NativeDaemonConnectorException e) {
1885 // Encryption failed
1886 return e.getCode();
1887 }
1888 }
1889
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001890 public Parcelable[] getVolumeList() {
1891 synchronized(mVolumes) {
1892 int size = mVolumes.size();
1893 Parcelable[] result = new Parcelable[size];
1894 for (int i = 0; i < size; i++) {
1895 result[i] = mVolumes.get(i);
Mike Lockwood8fa5f802011-03-24 08:12:30 -07001896 }
1897 return result;
1898 }
1899 }
1900
Kenny Rootaf9d6672010-10-08 09:21:39 -07001901 private void addObbStateLocked(ObbState obbState) throws RemoteException {
1902 final IBinder binder = obbState.getBinder();
1903 List<ObbState> obbStates = mObbMounts.get(binder);
Kenny Root5919ac62010-10-05 09:49:40 -07001904
Kenny Rootaf9d6672010-10-08 09:21:39 -07001905 if (obbStates == null) {
1906 obbStates = new ArrayList<ObbState>();
1907 mObbMounts.put(binder, obbStates);
1908 } else {
1909 for (final ObbState o : obbStates) {
1910 if (o.filename.equals(obbState.filename)) {
1911 throw new IllegalStateException("Attempt to add ObbState twice. "
1912 + "This indicates an error in the MountService logic.");
Kenny Root5919ac62010-10-05 09:49:40 -07001913 }
1914 }
Kenny Roota02b8b02010-08-05 16:14:17 -07001915 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07001916
1917 obbStates.add(obbState);
1918 try {
1919 obbState.link();
1920 } catch (RemoteException e) {
1921 /*
1922 * The binder died before we could link it, so clean up our state
1923 * and return failure.
1924 */
1925 obbStates.remove(obbState);
1926 if (obbStates.isEmpty()) {
1927 mObbMounts.remove(binder);
1928 }
1929
1930 // Rethrow the error so mountObb can get it
1931 throw e;
1932 }
1933
1934 mObbPathToStateMap.put(obbState.filename, obbState);
Kenny Roota02b8b02010-08-05 16:14:17 -07001935 }
1936
Kenny Rootaf9d6672010-10-08 09:21:39 -07001937 private void removeObbStateLocked(ObbState obbState) {
1938 final IBinder binder = obbState.getBinder();
1939 final List<ObbState> obbStates = mObbMounts.get(binder);
1940 if (obbStates != null) {
1941 if (obbStates.remove(obbState)) {
1942 obbState.unlink();
Kenny Root05105f72010-09-22 17:29:43 -07001943 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07001944 if (obbStates.isEmpty()) {
1945 mObbMounts.remove(binder);
1946 }
Kenny Roota02b8b02010-08-05 16:14:17 -07001947 }
Kenny Roota02b8b02010-08-05 16:14:17 -07001948
Kenny Rootaf9d6672010-10-08 09:21:39 -07001949 mObbPathToStateMap.remove(obbState.filename);
Kenny Root38cf8862010-09-26 14:18:51 -07001950 }
1951
Kenny Roota02b8b02010-08-05 16:14:17 -07001952 private class ObbActionHandler extends Handler {
1953 private boolean mBound = false;
Kenny Root480afe72010-10-07 10:17:50 -07001954 private final List<ObbAction> mActions = new LinkedList<ObbAction>();
Kenny Roota02b8b02010-08-05 16:14:17 -07001955
1956 ObbActionHandler(Looper l) {
1957 super(l);
1958 }
1959
1960 @Override
1961 public void handleMessage(Message msg) {
1962 switch (msg.what) {
1963 case OBB_RUN_ACTION: {
Kenny Root480afe72010-10-07 10:17:50 -07001964 final ObbAction action = (ObbAction) msg.obj;
Kenny Roota02b8b02010-08-05 16:14:17 -07001965
1966 if (DEBUG_OBB)
1967 Slog.i(TAG, "OBB_RUN_ACTION: " + action.toString());
1968
1969 // If a bind was already initiated we don't really
1970 // need to do anything. The pending install
1971 // will be processed later on.
1972 if (!mBound) {
1973 // If this is the only one pending we might
1974 // have to bind to the service again.
1975 if (!connectToService()) {
1976 Slog.e(TAG, "Failed to bind to media container service");
1977 action.handleError();
1978 return;
Kenny Roota02b8b02010-08-05 16:14:17 -07001979 }
Kenny Roota02b8b02010-08-05 16:14:17 -07001980 }
Kenny Root735de3b2010-09-30 14:11:39 -07001981
Kenny Root735de3b2010-09-30 14:11:39 -07001982 mActions.add(action);
Kenny Roota02b8b02010-08-05 16:14:17 -07001983 break;
1984 }
1985 case OBB_MCS_BOUND: {
1986 if (DEBUG_OBB)
1987 Slog.i(TAG, "OBB_MCS_BOUND");
1988 if (msg.obj != null) {
1989 mContainerService = (IMediaContainerService) msg.obj;
1990 }
1991 if (mContainerService == null) {
1992 // Something seriously wrong. Bail out
1993 Slog.e(TAG, "Cannot bind to media container service");
1994 for (ObbAction action : mActions) {
1995 // Indicate service bind error
1996 action.handleError();
1997 }
1998 mActions.clear();
1999 } else if (mActions.size() > 0) {
Kenny Root480afe72010-10-07 10:17:50 -07002000 final ObbAction action = mActions.get(0);
Kenny Roota02b8b02010-08-05 16:14:17 -07002001 if (action != null) {
2002 action.execute(this);
2003 }
2004 } else {
2005 // Should never happen ideally.
2006 Slog.w(TAG, "Empty queue");
2007 }
2008 break;
2009 }
2010 case OBB_MCS_RECONNECT: {
2011 if (DEBUG_OBB)
2012 Slog.i(TAG, "OBB_MCS_RECONNECT");
2013 if (mActions.size() > 0) {
2014 if (mBound) {
2015 disconnectService();
2016 }
2017 if (!connectToService()) {
2018 Slog.e(TAG, "Failed to bind to media container service");
2019 for (ObbAction action : mActions) {
2020 // Indicate service bind error
2021 action.handleError();
2022 }
2023 mActions.clear();
2024 }
2025 }
2026 break;
2027 }
2028 case OBB_MCS_UNBIND: {
2029 if (DEBUG_OBB)
2030 Slog.i(TAG, "OBB_MCS_UNBIND");
2031
2032 // Delete pending install
2033 if (mActions.size() > 0) {
2034 mActions.remove(0);
2035 }
2036 if (mActions.size() == 0) {
2037 if (mBound) {
2038 disconnectService();
2039 }
2040 } else {
2041 // There are more pending requests in queue.
2042 // Just post MCS_BOUND message to trigger processing
2043 // of next pending install.
2044 mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND);
2045 }
2046 break;
2047 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07002048 case OBB_FLUSH_MOUNT_STATE: {
2049 final String path = (String) msg.obj;
2050
2051 if (DEBUG_OBB)
2052 Slog.i(TAG, "Flushing all OBB state for path " + path);
2053
2054 synchronized (mObbMounts) {
2055 final List<ObbState> obbStatesToRemove = new LinkedList<ObbState>();
2056
2057 final Iterator<Entry<String, ObbState>> i =
2058 mObbPathToStateMap.entrySet().iterator();
2059 while (i.hasNext()) {
2060 final Entry<String, ObbState> obbEntry = i.next();
2061
2062 /*
2063 * If this entry's source file is in the volume path
2064 * that got unmounted, remove it because it's no
2065 * longer valid.
2066 */
2067 if (obbEntry.getKey().startsWith(path)) {
2068 obbStatesToRemove.add(obbEntry.getValue());
2069 }
2070 }
2071
2072 for (final ObbState obbState : obbStatesToRemove) {
2073 if (DEBUG_OBB)
2074 Slog.i(TAG, "Removing state for " + obbState.filename);
2075
2076 removeObbStateLocked(obbState);
2077
2078 try {
2079 obbState.token.onObbResult(obbState.filename, obbState.nonce,
2080 OnObbStateChangeListener.UNMOUNTED);
2081 } catch (RemoteException e) {
2082 Slog.i(TAG, "Couldn't send unmount notification for OBB: "
2083 + obbState.filename);
2084 }
2085 }
2086 }
2087 break;
2088 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002089 }
2090 }
2091
2092 private boolean connectToService() {
2093 if (DEBUG_OBB)
2094 Slog.i(TAG, "Trying to bind to DefaultContainerService");
2095
2096 Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
2097 if (mContext.bindService(service, mDefContainerConn, Context.BIND_AUTO_CREATE)) {
2098 mBound = true;
2099 return true;
2100 }
2101 return false;
2102 }
2103
2104 private void disconnectService() {
2105 mContainerService = null;
2106 mBound = false;
2107 mContext.unbindService(mDefContainerConn);
2108 }
2109 }
2110
2111 abstract class ObbAction {
2112 private static final int MAX_RETRIES = 3;
2113 private int mRetries;
2114
2115 ObbState mObbState;
2116
2117 ObbAction(ObbState obbState) {
2118 mObbState = obbState;
2119 }
2120
2121 public void execute(ObbActionHandler handler) {
2122 try {
2123 if (DEBUG_OBB)
Ben Komalo444eca22011-09-01 15:17:44 -07002124 Slog.i(TAG, "Starting to execute action: " + toString());
Kenny Roota02b8b02010-08-05 16:14:17 -07002125 mRetries++;
2126 if (mRetries > MAX_RETRIES) {
2127 Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
Kenny Root480afe72010-10-07 10:17:50 -07002128 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
Kenny Roota02b8b02010-08-05 16:14:17 -07002129 handleError();
2130 return;
2131 } else {
2132 handleExecute();
2133 if (DEBUG_OBB)
2134 Slog.i(TAG, "Posting install MCS_UNBIND");
2135 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
2136 }
2137 } catch (RemoteException e) {
2138 if (DEBUG_OBB)
2139 Slog.i(TAG, "Posting install MCS_RECONNECT");
2140 mObbActionHandler.sendEmptyMessage(OBB_MCS_RECONNECT);
2141 } catch (Exception e) {
2142 if (DEBUG_OBB)
2143 Slog.d(TAG, "Error handling OBB action", e);
2144 handleError();
Kenny Root17eb6fb2010-10-06 15:02:52 -07002145 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
Kenny Roota02b8b02010-08-05 16:14:17 -07002146 }
2147 }
2148
Kenny Root05105f72010-09-22 17:29:43 -07002149 abstract void handleExecute() throws RemoteException, IOException;
Kenny Roota02b8b02010-08-05 16:14:17 -07002150 abstract void handleError();
Kenny Root38cf8862010-09-26 14:18:51 -07002151
2152 protected ObbInfo getObbInfo() throws IOException {
2153 ObbInfo obbInfo;
2154 try {
2155 obbInfo = mContainerService.getObbInfo(mObbState.filename);
2156 } catch (RemoteException e) {
2157 Slog.d(TAG, "Couldn't call DefaultContainerService to fetch OBB info for "
2158 + mObbState.filename);
2159 obbInfo = null;
2160 }
2161 if (obbInfo == null) {
2162 throw new IOException("Couldn't read OBB file: " + mObbState.filename);
2163 }
2164 return obbInfo;
2165 }
2166
Kenny Rootaf9d6672010-10-08 09:21:39 -07002167 protected void sendNewStatusOrIgnore(int status) {
2168 if (mObbState == null || mObbState.token == null) {
2169 return;
2170 }
2171
Kenny Root38cf8862010-09-26 14:18:51 -07002172 try {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002173 mObbState.token.onObbResult(mObbState.filename, mObbState.nonce, status);
Kenny Root38cf8862010-09-26 14:18:51 -07002174 } catch (RemoteException e) {
2175 Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
2176 }
2177 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002178 }
2179
2180 class MountObbAction extends ObbAction {
Ben Komalo444eca22011-09-01 15:17:44 -07002181 private final String mKey;
Kenny Roota02b8b02010-08-05 16:14:17 -07002182
2183 MountObbAction(ObbState obbState, String key) {
2184 super(obbState);
2185 mKey = key;
2186 }
2187
Jason parks5af0b912010-11-29 09:05:25 -06002188 @Override
Kenny Root735de3b2010-09-30 14:11:39 -07002189 public void handleExecute() throws IOException, RemoteException {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002190 waitForReady();
2191 warnOnNotMounted();
2192
Kenny Root38cf8862010-09-26 14:18:51 -07002193 final ObbInfo obbInfo = getObbInfo();
2194
Kenny Roota02b8b02010-08-05 16:14:17 -07002195 if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mObbState.callerUid)) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002196 Slog.w(TAG, "Denied attempt to mount OBB " + obbInfo.filename
2197 + " which is owned by " + obbInfo.packageName);
2198 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
2199 return;
Kenny Roota02b8b02010-08-05 16:14:17 -07002200 }
2201
Kenny Rootaf9d6672010-10-08 09:21:39 -07002202 final boolean isMounted;
2203 synchronized (mObbMounts) {
2204 isMounted = mObbPathToStateMap.containsKey(obbInfo.filename);
2205 }
2206 if (isMounted) {
2207 Slog.w(TAG, "Attempt to mount OBB which is already mounted: " + obbInfo.filename);
2208 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_ALREADY_MOUNTED);
2209 return;
2210 }
2211
2212 /*
2213 * The filename passed in might not be the canonical name, so just
2214 * set the filename to the canonicalized version.
2215 */
2216 mObbState.filename = obbInfo.filename;
2217
2218 final String hashedKey;
2219 if (mKey == null) {
2220 hashedKey = "none";
2221 } else {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002222 try {
Kenny Root3b1abba2010-10-13 15:00:07 -07002223 SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
2224
2225 KeySpec ks = new PBEKeySpec(mKey.toCharArray(), obbInfo.salt,
2226 PBKDF2_HASH_ROUNDS, CRYPTO_ALGORITHM_KEY_SIZE);
2227 SecretKey key = factory.generateSecret(ks);
2228 BigInteger bi = new BigInteger(key.getEncoded());
2229 hashedKey = bi.toString(16);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002230 } catch (NoSuchAlgorithmException e) {
Kenny Root3b1abba2010-10-13 15:00:07 -07002231 Slog.e(TAG, "Could not load PBKDF2 algorithm", e);
2232 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
2233 return;
2234 } catch (InvalidKeySpecException e) {
2235 Slog.e(TAG, "Invalid key spec when loading PBKDF2 algorithm", e);
2236 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
Kenny Root38cf8862010-09-26 14:18:51 -07002237 return;
2238 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07002239 }
Kenny Root38cf8862010-09-26 14:18:51 -07002240
Kenny Rootaf9d6672010-10-08 09:21:39 -07002241 int rc = StorageResultCode.OperationSucceeded;
2242 String cmd = String.format("obb mount %s %s %d", mObbState.filename, hashedKey,
2243 mObbState.callerUid);
2244 try {
2245 mConnector.doCommand(cmd);
2246 } catch (NativeDaemonConnectorException e) {
2247 int code = e.getCode();
2248 if (code != VoldResponseCode.OpFailedStorageBusy) {
2249 rc = StorageResultCode.OperationFailedInternalError;
Kenny Roota02b8b02010-08-05 16:14:17 -07002250 }
2251 }
2252
Kenny Rootaf9d6672010-10-08 09:21:39 -07002253 if (rc == StorageResultCode.OperationSucceeded) {
2254 if (DEBUG_OBB)
2255 Slog.d(TAG, "Successfully mounted OBB " + mObbState.filename);
2256
2257 synchronized (mObbMounts) {
2258 addObbStateLocked(mObbState);
2259 }
2260
2261 sendNewStatusOrIgnore(OnObbStateChangeListener.MOUNTED);
Kenny Root02c87302010-07-01 08:10:18 -07002262 } else {
Kenny Root05105f72010-09-22 17:29:43 -07002263 Slog.e(TAG, "Couldn't mount OBB file: " + rc);
Kenny Roota02b8b02010-08-05 16:14:17 -07002264
Kenny Rootaf9d6672010-10-08 09:21:39 -07002265 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT);
Kenny Root02c87302010-07-01 08:10:18 -07002266 }
2267 }
2268
Jason parks5af0b912010-11-29 09:05:25 -06002269 @Override
Kenny Roota02b8b02010-08-05 16:14:17 -07002270 public void handleError() {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002271 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
Kenny Root02c87302010-07-01 08:10:18 -07002272 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002273
2274 @Override
2275 public String toString() {
2276 StringBuilder sb = new StringBuilder();
2277 sb.append("MountObbAction{");
2278 sb.append("filename=");
2279 sb.append(mObbState.filename);
2280 sb.append(",callerUid=");
2281 sb.append(mObbState.callerUid);
2282 sb.append(",token=");
2283 sb.append(mObbState.token != null ? mObbState.token.toString() : "NULL");
Kenny Rootaf9d6672010-10-08 09:21:39 -07002284 sb.append(",binder=");
2285 sb.append(mObbState.token != null ? mObbState.getBinder().toString() : "null");
Kenny Roota02b8b02010-08-05 16:14:17 -07002286 sb.append('}');
2287 return sb.toString();
2288 }
2289 }
2290
2291 class UnmountObbAction extends ObbAction {
Ben Komalo444eca22011-09-01 15:17:44 -07002292 private final boolean mForceUnmount;
Kenny Roota02b8b02010-08-05 16:14:17 -07002293
2294 UnmountObbAction(ObbState obbState, boolean force) {
2295 super(obbState);
2296 mForceUnmount = force;
2297 }
2298
Jason parks5af0b912010-11-29 09:05:25 -06002299 @Override
Kenny Root38cf8862010-09-26 14:18:51 -07002300 public void handleExecute() throws IOException {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002301 waitForReady();
2302 warnOnNotMounted();
2303
Kenny Root38cf8862010-09-26 14:18:51 -07002304 final ObbInfo obbInfo = getObbInfo();
Kenny Roota02b8b02010-08-05 16:14:17 -07002305
Kenny Rootaf9d6672010-10-08 09:21:39 -07002306 final ObbState obbState;
Kenny Root38cf8862010-09-26 14:18:51 -07002307 synchronized (mObbMounts) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002308 obbState = mObbPathToStateMap.get(obbInfo.filename);
2309 }
Kenny Root38cf8862010-09-26 14:18:51 -07002310
Kenny Rootaf9d6672010-10-08 09:21:39 -07002311 if (obbState == null) {
2312 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_NOT_MOUNTED);
2313 return;
2314 }
2315
2316 if (obbState.callerUid != mObbState.callerUid) {
2317 Slog.w(TAG, "Permission denied attempting to unmount OBB " + obbInfo.filename
2318 + " (owned by " + obbInfo.packageName + ")");
2319 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
2320 return;
2321 }
2322
2323 mObbState.filename = obbInfo.filename;
2324
2325 int rc = StorageResultCode.OperationSucceeded;
2326 String cmd = String.format("obb unmount %s%s", mObbState.filename,
2327 (mForceUnmount ? " force" : ""));
2328 try {
2329 mConnector.doCommand(cmd);
2330 } catch (NativeDaemonConnectorException e) {
2331 int code = e.getCode();
2332 if (code == VoldResponseCode.OpFailedStorageBusy) {
2333 rc = StorageResultCode.OperationFailedStorageBusy;
2334 } else if (code == VoldResponseCode.OpFailedStorageNotFound) {
2335 // If it's not mounted then we've already won.
2336 rc = StorageResultCode.OperationSucceeded;
2337 } else {
2338 rc = StorageResultCode.OperationFailedInternalError;
Kenny Roota02b8b02010-08-05 16:14:17 -07002339 }
2340 }
2341
Kenny Rootaf9d6672010-10-08 09:21:39 -07002342 if (rc == StorageResultCode.OperationSucceeded) {
2343 synchronized (mObbMounts) {
2344 removeObbStateLocked(obbState);
Kenny Root38cf8862010-09-26 14:18:51 -07002345 }
2346
Kenny Rootaf9d6672010-10-08 09:21:39 -07002347 sendNewStatusOrIgnore(OnObbStateChangeListener.UNMOUNTED);
Kenny Roota02b8b02010-08-05 16:14:17 -07002348 } else {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002349 Slog.w(TAG, "Could not mount OBB: " + mObbState.filename);
2350 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_UNMOUNT);
Kenny Roota02b8b02010-08-05 16:14:17 -07002351 }
2352 }
2353
Jason parks5af0b912010-11-29 09:05:25 -06002354 @Override
Kenny Roota02b8b02010-08-05 16:14:17 -07002355 public void handleError() {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002356 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
Kenny Roota02b8b02010-08-05 16:14:17 -07002357 }
2358
2359 @Override
2360 public String toString() {
2361 StringBuilder sb = new StringBuilder();
2362 sb.append("UnmountObbAction{");
2363 sb.append("filename=");
2364 sb.append(mObbState.filename != null ? mObbState.filename : "null");
2365 sb.append(",force=");
2366 sb.append(mForceUnmount);
2367 sb.append(",callerUid=");
2368 sb.append(mObbState.callerUid);
2369 sb.append(",token=");
2370 sb.append(mObbState.token != null ? mObbState.token.toString() : "null");
Kenny Root735de3b2010-09-30 14:11:39 -07002371 sb.append(",binder=");
Kenny Rootaf9d6672010-10-08 09:21:39 -07002372 sb.append(mObbState.token != null ? mObbState.getBinder().toString() : "null");
Kenny Roota02b8b02010-08-05 16:14:17 -07002373 sb.append('}');
2374 return sb.toString();
2375 }
Kenny Root02c87302010-07-01 08:10:18 -07002376 }
Kenny Root38cf8862010-09-26 14:18:51 -07002377
2378 @Override
2379 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2380 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) {
2381 pw.println("Permission Denial: can't dump ActivityManager from from pid="
2382 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
2383 + " without permission " + android.Manifest.permission.DUMP);
2384 return;
2385 }
2386
Kenny Root38cf8862010-09-26 14:18:51 -07002387 synchronized (mObbMounts) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002388 pw.println(" mObbMounts:");
Kenny Root38cf8862010-09-26 14:18:51 -07002389
Kenny Rootaf9d6672010-10-08 09:21:39 -07002390 final Iterator<Entry<IBinder, List<ObbState>>> binders = mObbMounts.entrySet().iterator();
2391 while (binders.hasNext()) {
2392 Entry<IBinder, List<ObbState>> e = binders.next();
2393 pw.print(" Key="); pw.println(e.getKey().toString());
2394 final List<ObbState> obbStates = e.getValue();
Kenny Root38cf8862010-09-26 14:18:51 -07002395 for (final ObbState obbState : obbStates) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002396 pw.print(" "); pw.println(obbState.toString());
Kenny Root38cf8862010-09-26 14:18:51 -07002397 }
2398 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07002399
2400 pw.println("");
2401 pw.println(" mObbPathToStateMap:");
2402 final Iterator<Entry<String, ObbState>> maps = mObbPathToStateMap.entrySet().iterator();
2403 while (maps.hasNext()) {
2404 final Entry<String, ObbState> e = maps.next();
2405 pw.print(" "); pw.print(e.getKey());
2406 pw.print(" -> "); pw.println(e.getValue().toString());
2407 }
Kenny Root38cf8862010-09-26 14:18:51 -07002408 }
Kenny Root4161f9b2011-07-13 09:48:33 -07002409
2410 pw.println("");
2411
2412 synchronized (mVolumes) {
2413 pw.println(" mVolumes:");
2414
2415 final int N = mVolumes.size();
2416 for (int i = 0; i < N; i++) {
2417 final StorageVolume v = mVolumes.get(i);
2418 pw.print(" ");
2419 pw.println(v.toString());
2420 }
2421 }
Kenny Root38cf8862010-09-26 14:18:51 -07002422 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002423
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -07002424 /** {@inheritDoc} */
2425 public void monitor() {
2426 if (mConnector != null) {
2427 mConnector.monitor();
2428 }
2429 }
2430}