blob: 7440f525c3d42808659bdd02bf45601c31a97128 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
Kenny Roota02b8b02010-08-05 16:14:17 -070019import com.android.internal.app.IMediaContainerService;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080020import com.android.server.am.ActivityManagerService;
21
Jason parks8888c592011-01-20 22:46:41 -060022import android.Manifest;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import android.content.BroadcastReceiver;
Kenny Roota02b8b02010-08-05 16:14:17 -070024import android.content.ComponentName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.content.Context;
26import android.content.Intent;
27import android.content.IntentFilter;
Kenny Roota02b8b02010-08-05 16:14:17 -070028import android.content.ServiceConnection;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.content.pm.PackageManager;
Kenny Root02c87302010-07-01 08:10:18 -070030import android.content.res.ObbInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import android.net.Uri;
Kenny Root02c87302010-07-01 08:10:18 -070032import android.os.Binder;
Kenny Roota02b8b02010-08-05 16:14:17 -070033import android.os.Environment;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080034import android.os.Handler;
Daniel Sandler5f27ef42010-03-16 15:42:02 -040035import android.os.HandlerThread;
Kenny Roota02b8b02010-08-05 16:14:17 -070036import android.os.IBinder;
Daniel Sandler5f27ef42010-03-16 15:42:02 -040037import android.os.Looper;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080038import android.os.Message;
San Mehat4270e1e2010-01-29 05:32:19 -080039import android.os.RemoteException;
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -080040import android.os.ServiceManager;
San Mehat207e5382010-02-04 20:46:54 -080041import android.os.SystemClock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042import android.os.SystemProperties;
Kenny Roota02b8b02010-08-05 16:14:17 -070043import android.os.storage.IMountService;
44import android.os.storage.IMountServiceListener;
45import android.os.storage.IMountShutdownObserver;
46import android.os.storage.IObbActionListener;
Kenny Rootaf9d6672010-10-08 09:21:39 -070047import android.os.storage.OnObbStateChangeListener;
Kenny Roota02b8b02010-08-05 16:14:17 -070048import android.os.storage.StorageResultCode;
San Mehata5078592010-03-25 09:36:54 -070049import android.util.Slog;
Kenny Roota02b8b02010-08-05 16:14:17 -070050
Kenny Root38cf8862010-09-26 14:18:51 -070051import java.io.FileDescriptor;
Kenny Root05105f72010-09-22 17:29:43 -070052import java.io.IOException;
Kenny Root38cf8862010-09-26 14:18:51 -070053import java.io.PrintWriter;
Kenny Root3b1abba2010-10-13 15:00:07 -070054import java.math.BigInteger;
Kenny Root735de3b2010-09-30 14:11:39 -070055import java.security.NoSuchAlgorithmException;
Kenny Root3b1abba2010-10-13 15:00:07 -070056import java.security.spec.InvalidKeySpecException;
57import java.security.spec.KeySpec;
San Mehat22dd86e2010-01-12 12:21:18 -080058import java.util.ArrayList;
Kenny Roota02b8b02010-08-05 16:14:17 -070059import java.util.HashMap;
San Mehat6cdd9c02010-02-09 14:45:20 -080060import java.util.HashSet;
Kenny Root38cf8862010-09-26 14:18:51 -070061import java.util.Iterator;
Kenny Roota02b8b02010-08-05 16:14:17 -070062import java.util.LinkedList;
63import java.util.List;
64import java.util.Map;
Kenny Root38cf8862010-09-26 14:18:51 -070065import java.util.Map.Entry;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066
Kenny Root3b1abba2010-10-13 15:00:07 -070067import javax.crypto.SecretKey;
68import javax.crypto.SecretKeyFactory;
69import javax.crypto.spec.PBEKeySpec;
70
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071/**
San Mehatb1043402010-02-05 08:26:50 -080072 * MountService implements back-end services for platform storage
73 * management.
74 * @hide - Applications should use android.os.storage.StorageManager
75 * to access the MountService.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076 */
Jason parks5af0b912010-11-29 09:05:25 -060077class MountService extends IMountService.Stub implements INativeDaemonConnectorCallbacks {
78
San Mehatb1043402010-02-05 08:26:50 -080079 private static final boolean LOCAL_LOGD = false;
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -080080 private static final boolean DEBUG_UNMOUNT = false;
81 private static final boolean DEBUG_EVENTS = false;
Kenny Root02c87302010-07-01 08:10:18 -070082 private static final boolean DEBUG_OBB = true;
83
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084 private static final String TAG = "MountService";
85
Kenny Root305bcbf2010-09-03 07:56:38 -070086 private static final String VOLD_TAG = "VoldConnector";
87
San Mehat4270e1e2010-01-29 05:32:19 -080088 /*
89 * Internal vold volume state constants
90 */
San Mehat7fd0fee2009-12-17 07:12:23 -080091 class VolumeState {
92 public static final int Init = -1;
93 public static final int NoMedia = 0;
94 public static final int Idle = 1;
95 public static final int Pending = 2;
96 public static final int Checking = 3;
97 public static final int Mounted = 4;
98 public static final int Unmounting = 5;
99 public static final int Formatting = 6;
100 public static final int Shared = 7;
101 public static final int SharedMnt = 8;
102 }
103
San Mehat4270e1e2010-01-29 05:32:19 -0800104 /*
105 * Internal vold response code constants
106 */
San Mehat22dd86e2010-01-12 12:21:18 -0800107 class VoldResponseCode {
San Mehat4270e1e2010-01-29 05:32:19 -0800108 /*
109 * 100 series - Requestion action was initiated; expect another reply
110 * before proceeding with a new command.
111 */
San Mehat22dd86e2010-01-12 12:21:18 -0800112 public static final int VolumeListResult = 110;
113 public static final int AsecListResult = 111;
San Mehatc1b4ce92010-02-16 17:13:03 -0800114 public static final int StorageUsersListResult = 112;
San Mehat22dd86e2010-01-12 12:21:18 -0800115
San Mehat4270e1e2010-01-29 05:32:19 -0800116 /*
117 * 200 series - Requestion action has been successfully completed.
118 */
119 public static final int ShareStatusResult = 210;
San Mehat22dd86e2010-01-12 12:21:18 -0800120 public static final int AsecPathResult = 211;
San Mehat4270e1e2010-01-29 05:32:19 -0800121 public static final int ShareEnabledResult = 212;
San Mehat22dd86e2010-01-12 12:21:18 -0800122
San Mehat4270e1e2010-01-29 05:32:19 -0800123 /*
124 * 400 series - Command was accepted, but the requested action
125 * did not take place.
126 */
127 public static final int OpFailedNoMedia = 401;
128 public static final int OpFailedMediaBlank = 402;
129 public static final int OpFailedMediaCorrupt = 403;
130 public static final int OpFailedVolNotMounted = 404;
San Mehatd9709982010-02-18 11:43:03 -0800131 public static final int OpFailedStorageBusy = 405;
San Mehat2d66cef2010-03-23 11:12:52 -0700132 public static final int OpFailedStorageNotFound = 406;
San Mehat4270e1e2010-01-29 05:32:19 -0800133
134 /*
135 * 600 series - Unsolicited broadcasts.
136 */
San Mehat22dd86e2010-01-12 12:21:18 -0800137 public static final int VolumeStateChange = 605;
San Mehat22dd86e2010-01-12 12:21:18 -0800138 public static final int ShareAvailabilityChange = 620;
139 public static final int VolumeDiskInserted = 630;
140 public static final int VolumeDiskRemoved = 631;
141 public static final int VolumeBadRemoval = 632;
142 }
143
San Mehat4270e1e2010-01-29 05:32:19 -0800144 private Context mContext;
145 private NativeDaemonConnector mConnector;
146 private String mLegacyState = Environment.MEDIA_REMOVED;
147 private PackageManagerService mPms;
148 private boolean mUmsEnabling;
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800149 // Used as a lock for methods that register/unregister listeners.
150 final private ArrayList<MountServiceBinderListener> mListeners =
151 new ArrayList<MountServiceBinderListener>();
San Mehat6a965af22010-02-24 17:47:30 -0800152 private boolean mBooted = false;
153 private boolean mReady = false;
154 private boolean mSendUmsConnectedOnBoot = false;
Mike Lockwood03559752010-07-19 18:25:03 -0400155 // true if we should fake MEDIA_MOUNTED state for external storage
156 private boolean mEmulateExternalStorage = false;
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -0800157
San Mehat6cdd9c02010-02-09 14:45:20 -0800158 /**
159 * Private hash of currently mounted secure containers.
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800160 * Used as a lock in methods to manipulate secure containers.
San Mehat6cdd9c02010-02-09 14:45:20 -0800161 */
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800162 final private HashSet<String> mAsecMountSet = new HashSet<String>();
San Mehat6cdd9c02010-02-09 14:45:20 -0800163
Kenny Root02c87302010-07-01 08:10:18 -0700164 /**
Kenny Root3b1abba2010-10-13 15:00:07 -0700165 * The size of the crypto algorithm key in bits for OBB files. Currently
166 * Twofish is used which takes 128-bit keys.
167 */
168 private static final int CRYPTO_ALGORITHM_KEY_SIZE = 128;
169
170 /**
171 * The number of times to run SHA1 in the PBKDF2 function for OBB files.
172 * 1024 is reasonably secure and not too slow.
173 */
174 private static final int PBKDF2_HASH_ROUNDS = 1024;
175
176 /**
Kenny Roota02b8b02010-08-05 16:14:17 -0700177 * Mounted OBB tracking information. Used to track the current state of all
178 * OBBs.
Kenny Root02c87302010-07-01 08:10:18 -0700179 */
Kenny Root735de3b2010-09-30 14:11:39 -0700180 final private Map<IBinder, List<ObbState>> mObbMounts = new HashMap<IBinder, List<ObbState>>();
Kenny Roota02b8b02010-08-05 16:14:17 -0700181 final private Map<String, ObbState> mObbPathToStateMap = new HashMap<String, ObbState>();
182
183 class ObbState implements IBinder.DeathRecipient {
Kenny Rootaf9d6672010-10-08 09:21:39 -0700184 public ObbState(String filename, int callerUid, IObbActionListener token, int nonce)
Kenny Root735de3b2010-09-30 14:11:39 -0700185 throws RemoteException {
Kenny Roota02b8b02010-08-05 16:14:17 -0700186 this.filename = filename;
Kenny Roota02b8b02010-08-05 16:14:17 -0700187 this.callerUid = callerUid;
Kenny Rootaf9d6672010-10-08 09:21:39 -0700188 this.token = token;
189 this.nonce = nonce;
Kenny Roota02b8b02010-08-05 16:14:17 -0700190 }
191
192 // OBB source filename
Kenny Rootaf9d6672010-10-08 09:21:39 -0700193 String filename;
Kenny Roota02b8b02010-08-05 16:14:17 -0700194
195 // Binder.callingUid()
Kenny Root05105f72010-09-22 17:29:43 -0700196 final public int callerUid;
Kenny Roota02b8b02010-08-05 16:14:17 -0700197
Kenny Rootaf9d6672010-10-08 09:21:39 -0700198 // Token of remote Binder caller
199 final IObbActionListener token;
200
201 // Identifier to pass back to the token
202 final int nonce;
Kenny Roota02b8b02010-08-05 16:14:17 -0700203
Kenny Root735de3b2010-09-30 14:11:39 -0700204 public IBinder getBinder() {
205 return token.asBinder();
206 }
207
Kenny Roota02b8b02010-08-05 16:14:17 -0700208 @Override
209 public void binderDied() {
210 ObbAction action = new UnmountObbAction(this, true);
211 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
Kenny Root735de3b2010-09-30 14:11:39 -0700212 }
Kenny Roota02b8b02010-08-05 16:14:17 -0700213
Kenny Root5919ac62010-10-05 09:49:40 -0700214 public void link() throws RemoteException {
215 getBinder().linkToDeath(this, 0);
216 }
217
218 public void unlink() {
Kenny Root735de3b2010-09-30 14:11:39 -0700219 getBinder().unlinkToDeath(this, 0);
Kenny Roota02b8b02010-08-05 16:14:17 -0700220 }
Kenny Root38cf8862010-09-26 14:18:51 -0700221
222 @Override
223 public String toString() {
224 StringBuilder sb = new StringBuilder("ObbState{");
225 sb.append("filename=");
226 sb.append(filename);
227 sb.append(",token=");
228 sb.append(token.toString());
229 sb.append(",callerUid=");
230 sb.append(callerUid);
Kenny Root38cf8862010-09-26 14:18:51 -0700231 sb.append('}');
232 return sb.toString();
233 }
Kenny Roota02b8b02010-08-05 16:14:17 -0700234 }
235
236 // OBB Action Handler
237 final private ObbActionHandler mObbActionHandler;
238
239 // OBB action handler messages
240 private static final int OBB_RUN_ACTION = 1;
241 private static final int OBB_MCS_BOUND = 2;
242 private static final int OBB_MCS_UNBIND = 3;
243 private static final int OBB_MCS_RECONNECT = 4;
Kenny Rootaf9d6672010-10-08 09:21:39 -0700244 private static final int OBB_FLUSH_MOUNT_STATE = 5;
Kenny Roota02b8b02010-08-05 16:14:17 -0700245
246 /*
247 * Default Container Service information
248 */
249 static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
250 "com.android.defcontainer", "com.android.defcontainer.DefaultContainerService");
251
252 final private DefaultContainerConnection mDefContainerConn = new DefaultContainerConnection();
253
254 class DefaultContainerConnection implements ServiceConnection {
255 public void onServiceConnected(ComponentName name, IBinder service) {
256 if (DEBUG_OBB)
257 Slog.i(TAG, "onServiceConnected");
258 IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(service);
259 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_MCS_BOUND, imcs));
260 }
261
262 public void onServiceDisconnected(ComponentName name) {
263 if (DEBUG_OBB)
264 Slog.i(TAG, "onServiceDisconnected");
265 }
266 };
267
268 // Used in the ObbActionHandler
269 private IMediaContainerService mContainerService = null;
Kenny Root02c87302010-07-01 08:10:18 -0700270
271 // Handler messages
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800272 private static final int H_UNMOUNT_PM_UPDATE = 1;
273 private static final int H_UNMOUNT_PM_DONE = 2;
274 private static final int H_UNMOUNT_MS = 3;
275 private static final int RETRY_UNMOUNT_DELAY = 30; // in ms
276 private static final int MAX_UNMOUNT_RETRIES = 4;
277
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800278 class UnmountCallBack {
Kenny Root05105f72010-09-22 17:29:43 -0700279 final String path;
280 final boolean force;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800281 int retries;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800282
283 UnmountCallBack(String path, boolean force) {
284 retries = 0;
285 this.path = path;
286 this.force = force;
287 }
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800288
289 void handleFinished() {
San Mehata5078592010-03-25 09:36:54 -0700290 if (DEBUG_UNMOUNT) Slog.i(TAG, "Unmounting " + path);
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800291 doUnmountVolume(path, true);
292 }
293 }
294
295 class UmsEnableCallBack extends UnmountCallBack {
Kenny Root05105f72010-09-22 17:29:43 -0700296 final String method;
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800297
298 UmsEnableCallBack(String path, String method, boolean force) {
299 super(path, force);
300 this.method = method;
301 }
302
303 @Override
304 void handleFinished() {
305 super.handleFinished();
306 doShareUnshareVolume(path, method, true);
307 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800308 }
309
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800310 class ShutdownCallBack extends UnmountCallBack {
311 IMountShutdownObserver observer;
312 ShutdownCallBack(String path, IMountShutdownObserver observer) {
313 super(path, true);
314 this.observer = observer;
315 }
316
317 @Override
318 void handleFinished() {
319 int ret = doUnmountVolume(path, true);
320 if (observer != null) {
321 try {
322 observer.onShutDownComplete(ret);
323 } catch (RemoteException e) {
San Mehata5078592010-03-25 09:36:54 -0700324 Slog.w(TAG, "RemoteException when shutting down");
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800325 }
326 }
327 }
328 }
329
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400330 class MountServiceHandler extends Handler {
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800331 ArrayList<UnmountCallBack> mForceUnmounts = new ArrayList<UnmountCallBack>();
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700332 boolean mUpdatingStatus = false;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800333
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400334 MountServiceHandler(Looper l) {
335 super(l);
336 }
337
Jason parks5af0b912010-11-29 09:05:25 -0600338 @Override
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800339 public void handleMessage(Message msg) {
340 switch (msg.what) {
341 case H_UNMOUNT_PM_UPDATE: {
San Mehata5078592010-03-25 09:36:54 -0700342 if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_UPDATE");
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800343 UnmountCallBack ucb = (UnmountCallBack) msg.obj;
344 mForceUnmounts.add(ucb);
San Mehata5078592010-03-25 09:36:54 -0700345 if (DEBUG_UNMOUNT) Slog.i(TAG, " registered = " + mUpdatingStatus);
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800346 // Register only if needed.
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700347 if (!mUpdatingStatus) {
San Mehata5078592010-03-25 09:36:54 -0700348 if (DEBUG_UNMOUNT) Slog.i(TAG, "Updating external media status on PackageManager");
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700349 mUpdatingStatus = true;
350 mPms.updateExternalMediaStatus(false, true);
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800351 }
352 break;
353 }
354 case H_UNMOUNT_PM_DONE: {
San Mehata5078592010-03-25 09:36:54 -0700355 if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_DONE");
San Mehata5078592010-03-25 09:36:54 -0700356 if (DEBUG_UNMOUNT) Slog.i(TAG, "Updated status. Processing requests");
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700357 mUpdatingStatus = false;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800358 int size = mForceUnmounts.size();
359 int sizeArr[] = new int[size];
360 int sizeArrN = 0;
Suchi Amalapurapu7af074a2010-04-05 16:46:32 -0700361 // Kill processes holding references first
362 ActivityManagerService ams = (ActivityManagerService)
363 ServiceManager.getService("activity");
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800364 for (int i = 0; i < size; i++) {
365 UnmountCallBack ucb = mForceUnmounts.get(i);
366 String path = ucb.path;
367 boolean done = false;
368 if (!ucb.force) {
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800369 done = true;
370 } else {
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800371 int pids[] = getStorageUsers(path);
372 if (pids == null || pids.length == 0) {
373 done = true;
374 } else {
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800375 // Eliminate system process here?
Suchi Amalapurapu7af074a2010-04-05 16:46:32 -0700376 ams.killPids(pids, "unmount media");
377 // Confirm if file references have been freed.
378 pids = getStorageUsers(path);
379 if (pids == null || pids.length == 0) {
380 done = true;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800381 }
382 }
383 }
Suchi Amalapurapu7af074a2010-04-05 16:46:32 -0700384 if (!done && (ucb.retries < MAX_UNMOUNT_RETRIES)) {
385 // Retry again
386 Slog.i(TAG, "Retrying to kill storage users again");
387 mHandler.sendMessageDelayed(
388 mHandler.obtainMessage(H_UNMOUNT_PM_DONE,
389 ucb.retries++),
390 RETRY_UNMOUNT_DELAY);
391 } else {
392 if (ucb.retries >= MAX_UNMOUNT_RETRIES) {
393 Slog.i(TAG, "Failed to unmount media inspite of " +
394 MAX_UNMOUNT_RETRIES + " retries. Forcibly killing processes now");
395 }
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800396 sizeArr[sizeArrN++] = i;
397 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_MS,
398 ucb));
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800399 }
400 }
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800401 // Remove already processed elements from list.
402 for (int i = (sizeArrN-1); i >= 0; i--) {
403 mForceUnmounts.remove(sizeArr[i]);
404 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800405 break;
406 }
407 case H_UNMOUNT_MS : {
San Mehata5078592010-03-25 09:36:54 -0700408 if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_MS");
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800409 UnmountCallBack ucb = (UnmountCallBack) msg.obj;
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800410 ucb.handleFinished();
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800411 break;
412 }
413 }
414 }
415 };
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400416 final private HandlerThread mHandlerThread;
417 final private Handler mHandler;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800418
San Mehat207e5382010-02-04 20:46:54 -0800419 private void waitForReady() {
420 while (mReady == false) {
421 for (int retries = 5; retries > 0; retries--) {
422 if (mReady) {
423 return;
424 }
425 SystemClock.sleep(1000);
426 }
San Mehata5078592010-03-25 09:36:54 -0700427 Slog.w(TAG, "Waiting too long for mReady!");
San Mehat207e5382010-02-04 20:46:54 -0800428 }
San Mehat1f6301e2010-01-07 22:40:27 -0800429 }
Kenny Root02c87302010-07-01 08:10:18 -0700430
San Mehat207e5382010-02-04 20:46:54 -0800431 private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
Jason parks5af0b912010-11-29 09:05:25 -0600432 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800433 public void onReceive(Context context, Intent intent) {
San Mehat91c77612010-01-07 10:39:41 -0800434 String action = intent.getAction();
435
436 if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
San Mehat207e5382010-02-04 20:46:54 -0800437 mBooted = true;
San Mehat22dd86e2010-01-12 12:21:18 -0800438
Marco Nelissenc34ebce2010-02-18 13:39:41 -0800439 /*
440 * In the simulator, we need to broadcast a volume mounted event
441 * to make the media scanner run.
442 */
443 if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
444 notifyVolumeStateChange(null, "/sdcard", VolumeState.NoMedia, VolumeState.Mounted);
445 return;
446 }
San Mehatfafb0412010-02-18 19:40:04 -0800447 new Thread() {
Jason parks5af0b912010-11-29 09:05:25 -0600448 @Override
San Mehatfafb0412010-02-18 19:40:04 -0800449 public void run() {
450 try {
451 String path = Environment.getExternalStorageDirectory().getPath();
San Mehat6a254402010-03-22 10:21:00 -0700452 String state = getVolumeState(path);
453
Mike Lockwood03559752010-07-19 18:25:03 -0400454 if (mEmulateExternalStorage) {
455 notifyVolumeStateChange(null, path, VolumeState.NoMedia, VolumeState.Mounted);
456 } else if (state.equals(Environment.MEDIA_UNMOUNTED)) {
San Mehatfafb0412010-02-18 19:40:04 -0800457 int rc = doMountVolume(path);
458 if (rc != StorageResultCode.OperationSucceeded) {
San Mehata5078592010-03-25 09:36:54 -0700459 Slog.e(TAG, String.format("Boot-time mount failed (%d)", rc));
San Mehatfafb0412010-02-18 19:40:04 -0800460 }
San Mehat6a254402010-03-22 10:21:00 -0700461 } else if (state.equals(Environment.MEDIA_SHARED)) {
462 /*
463 * Bootstrap UMS enabled state since vold indicates
464 * the volume is shared (runtime restart while ums enabled)
465 */
466 notifyVolumeStateChange(null, path, VolumeState.NoMedia, VolumeState.Shared);
San Mehatfafb0412010-02-18 19:40:04 -0800467 }
San Mehat6a254402010-03-22 10:21:00 -0700468
San Mehat6a965af22010-02-24 17:47:30 -0800469 /*
San Mehat6a254402010-03-22 10:21:00 -0700470 * If UMS was connected on boot, send the connected event
San Mehat6a965af22010-02-24 17:47:30 -0800471 * now that we're up.
472 */
473 if (mSendUmsConnectedOnBoot) {
474 sendUmsIntent(true);
475 mSendUmsConnectedOnBoot = false;
476 }
San Mehatfafb0412010-02-18 19:40:04 -0800477 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700478 Slog.e(TAG, "Boot-time mount exception", ex);
San Mehatfafb0412010-02-18 19:40:04 -0800479 }
San Mehat207e5382010-02-04 20:46:54 -0800480 }
San Mehatfafb0412010-02-18 19:40:04 -0800481 }.start();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800482 }
483 }
484 };
485
San Mehat4270e1e2010-01-29 05:32:19 -0800486 private final class MountServiceBinderListener implements IBinder.DeathRecipient {
487 final IMountServiceListener mListener;
488
489 MountServiceBinderListener(IMountServiceListener listener) {
490 mListener = listener;
Kenny Root02c87302010-07-01 08:10:18 -0700491
San Mehat91c77612010-01-07 10:39:41 -0800492 }
493
San Mehat4270e1e2010-01-29 05:32:19 -0800494 public void binderDied() {
San Mehata5078592010-03-25 09:36:54 -0700495 if (LOCAL_LOGD) Slog.d(TAG, "An IMountServiceListener has died!");
Kenny Roota02b8b02010-08-05 16:14:17 -0700496 synchronized (mListeners) {
San Mehat4270e1e2010-01-29 05:32:19 -0800497 mListeners.remove(this);
498 mListener.asBinder().unlinkToDeath(this, 0);
499 }
500 }
501 }
502
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800503 private void doShareUnshareVolume(String path, String method, boolean enable) {
San Mehat4270e1e2010-01-29 05:32:19 -0800504 // TODO: Add support for multiple share methods
505 if (!method.equals("ums")) {
506 throw new IllegalArgumentException(String.format("Method %s not supported", method));
507 }
508
San Mehat4270e1e2010-01-29 05:32:19 -0800509 try {
510 mConnector.doCommand(String.format(
511 "volume %sshare %s %s", (enable ? "" : "un"), path, method));
512 } catch (NativeDaemonConnectorException e) {
San Mehata5078592010-03-25 09:36:54 -0700513 Slog.e(TAG, "Failed to share/unshare", e);
San Mehat4270e1e2010-01-29 05:32:19 -0800514 }
San Mehat4270e1e2010-01-29 05:32:19 -0800515 }
516
San Mehat207e5382010-02-04 20:46:54 -0800517 private void updatePublicVolumeState(String path, String state) {
San Mehat4270e1e2010-01-29 05:32:19 -0800518 if (!path.equals(Environment.getExternalStorageDirectory().getPath())) {
San Mehata5078592010-03-25 09:36:54 -0700519 Slog.w(TAG, "Multiple volumes not currently supported");
San Mehat4270e1e2010-01-29 05:32:19 -0800520 return;
521 }
San Mehatb1043402010-02-05 08:26:50 -0800522
523 if (mLegacyState.equals(state)) {
San Mehata5078592010-03-25 09:36:54 -0700524 Slog.w(TAG, String.format("Duplicate state transition (%s -> %s)", mLegacyState, state));
San Mehatb1043402010-02-05 08:26:50 -0800525 return;
526 }
Mike Lockwood03559752010-07-19 18:25:03 -0400527 // Update state on PackageManager, but only of real events
528 if (!mEmulateExternalStorage) {
529 if (Environment.MEDIA_UNMOUNTED.equals(state)) {
530 mPms.updateExternalMediaStatus(false, false);
Kenny Rootaf9d6672010-10-08 09:21:39 -0700531
Kenny Root0a9b54e2010-10-13 15:43:35 -0700532 /*
533 * Some OBBs might have been unmounted when this volume was
534 * unmounted, so send a message to the handler to let it know to
535 * remove those from the list of mounted OBBS.
536 */
537 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_FLUSH_MOUNT_STATE,
538 path));
Mike Lockwood03559752010-07-19 18:25:03 -0400539 } else if (Environment.MEDIA_MOUNTED.equals(state)) {
540 mPms.updateExternalMediaStatus(true, false);
541 }
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800542 }
Kenny Root38cf8862010-09-26 14:18:51 -0700543
San Mehat4270e1e2010-01-29 05:32:19 -0800544 String oldState = mLegacyState;
545 mLegacyState = state;
546
547 synchronized (mListeners) {
548 for (int i = mListeners.size() -1; i >= 0; i--) {
549 MountServiceBinderListener bl = mListeners.get(i);
550 try {
San Mehatb1043402010-02-05 08:26:50 -0800551 bl.mListener.onStorageStateChanged(path, oldState, state);
San Mehat4270e1e2010-01-29 05:32:19 -0800552 } catch (RemoteException rex) {
San Mehata5078592010-03-25 09:36:54 -0700553 Slog.e(TAG, "Listener dead");
San Mehat4270e1e2010-01-29 05:32:19 -0800554 mListeners.remove(i);
555 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700556 Slog.e(TAG, "Listener failed", ex);
San Mehat4270e1e2010-01-29 05:32:19 -0800557 }
558 }
559 }
560 }
561
562 /**
563 *
564 * Callback from NativeDaemonConnector
565 */
566 public void onDaemonConnected() {
567 /*
568 * Since we'll be calling back into the NativeDaemonConnector,
569 * we need to do our work in a new thread.
570 */
571 new Thread() {
Jason parks5af0b912010-11-29 09:05:25 -0600572 @Override
San Mehat4270e1e2010-01-29 05:32:19 -0800573 public void run() {
574 /**
575 * Determine media state and UMS detection status
576 */
577 String path = Environment.getExternalStorageDirectory().getPath();
578 String state = Environment.MEDIA_REMOVED;
579
580 try {
581 String[] vols = mConnector.doListCommand(
582 "volume list", VoldResponseCode.VolumeListResult);
583 for (String volstr : vols) {
584 String[] tok = volstr.split(" ");
585 // FMT: <label> <mountpoint> <state>
586 if (!tok[1].equals(path)) {
San Mehata5078592010-03-25 09:36:54 -0700587 Slog.w(TAG, String.format(
San Mehat4270e1e2010-01-29 05:32:19 -0800588 "Skipping unknown volume '%s'",tok[1]));
589 continue;
590 }
591 int st = Integer.parseInt(tok[2]);
592 if (st == VolumeState.NoMedia) {
593 state = Environment.MEDIA_REMOVED;
594 } else if (st == VolumeState.Idle) {
San Mehat207e5382010-02-04 20:46:54 -0800595 state = Environment.MEDIA_UNMOUNTED;
San Mehat4270e1e2010-01-29 05:32:19 -0800596 } else if (st == VolumeState.Mounted) {
597 state = Environment.MEDIA_MOUNTED;
San Mehata5078592010-03-25 09:36:54 -0700598 Slog.i(TAG, "Media already mounted on daemon connection");
San Mehat4270e1e2010-01-29 05:32:19 -0800599 } else if (st == VolumeState.Shared) {
600 state = Environment.MEDIA_SHARED;
San Mehata5078592010-03-25 09:36:54 -0700601 Slog.i(TAG, "Media shared on daemon connection");
San Mehat4270e1e2010-01-29 05:32:19 -0800602 } else {
603 throw new Exception(String.format("Unexpected state %d", st));
604 }
605 }
606 if (state != null) {
San Mehata5078592010-03-25 09:36:54 -0700607 if (DEBUG_EVENTS) Slog.i(TAG, "Updating valid state " + state);
San Mehat4270e1e2010-01-29 05:32:19 -0800608 updatePublicVolumeState(path, state);
609 }
610 } catch (Exception e) {
San Mehata5078592010-03-25 09:36:54 -0700611 Slog.e(TAG, "Error processing initial volume state", e);
San Mehat4270e1e2010-01-29 05:32:19 -0800612 updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
613 }
614
615 try {
San Mehat207e5382010-02-04 20:46:54 -0800616 boolean avail = doGetShareMethodAvailable("ums");
San Mehat4270e1e2010-01-29 05:32:19 -0800617 notifyShareAvailabilityChange("ums", avail);
618 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700619 Slog.w(TAG, "Failed to get share availability");
San Mehat4270e1e2010-01-29 05:32:19 -0800620 }
San Mehat207e5382010-02-04 20:46:54 -0800621 /*
Jason parks9ed98bc2011-01-17 09:58:35 -0600622 * Now that we've done our initialization, release
San Mehat207e5382010-02-04 20:46:54 -0800623 * the hounds!
624 */
625 mReady = true;
San Mehat4270e1e2010-01-29 05:32:19 -0800626 }
627 }.start();
628 }
629
630 /**
San Mehat4270e1e2010-01-29 05:32:19 -0800631 * Callback from NativeDaemonConnector
632 */
633 public boolean onEvent(int code, String raw, String[] cooked) {
634 Intent in = null;
635
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800636 if (DEBUG_EVENTS) {
637 StringBuilder builder = new StringBuilder();
638 builder.append("onEvent::");
639 builder.append(" raw= " + raw);
640 if (cooked != null) {
641 builder.append(" cooked = " );
642 for (String str : cooked) {
643 builder.append(" " + str);
644 }
645 }
San Mehata5078592010-03-25 09:36:54 -0700646 Slog.i(TAG, builder.toString());
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800647 }
San Mehat4270e1e2010-01-29 05:32:19 -0800648 if (code == VoldResponseCode.VolumeStateChange) {
649 /*
650 * One of the volumes we're managing has changed state.
651 * Format: "NNN Volume <label> <path> state changed
652 * from <old_#> (<old_str>) to <new_#> (<new_str>)"
653 */
654 notifyVolumeStateChange(
655 cooked[2], cooked[3], Integer.parseInt(cooked[7]),
656 Integer.parseInt(cooked[10]));
657 } else if (code == VoldResponseCode.ShareAvailabilityChange) {
658 // FMT: NNN Share method <method> now <available|unavailable>
659 boolean avail = false;
660 if (cooked[5].equals("available")) {
661 avail = true;
662 }
663 notifyShareAvailabilityChange(cooked[3], avail);
664 } else if ((code == VoldResponseCode.VolumeDiskInserted) ||
665 (code == VoldResponseCode.VolumeDiskRemoved) ||
666 (code == VoldResponseCode.VolumeBadRemoval)) {
667 // FMT: NNN Volume <label> <mountpoint> disk inserted (<major>:<minor>)
668 // FMT: NNN Volume <label> <mountpoint> disk removed (<major>:<minor>)
669 // FMT: NNN Volume <label> <mountpoint> bad removal (<major>:<minor>)
670 final String label = cooked[2];
671 final String path = cooked[3];
672 int major = -1;
673 int minor = -1;
674
675 try {
676 String devComp = cooked[6].substring(1, cooked[6].length() -1);
677 String[] devTok = devComp.split(":");
678 major = Integer.parseInt(devTok[0]);
679 minor = Integer.parseInt(devTok[1]);
680 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700681 Slog.e(TAG, "Failed to parse major/minor", ex);
San Mehat4270e1e2010-01-29 05:32:19 -0800682 }
683
San Mehat4270e1e2010-01-29 05:32:19 -0800684 if (code == VoldResponseCode.VolumeDiskInserted) {
685 new Thread() {
Jason parks5af0b912010-11-29 09:05:25 -0600686 @Override
San Mehat4270e1e2010-01-29 05:32:19 -0800687 public void run() {
688 try {
689 int rc;
San Mehatb1043402010-02-05 08:26:50 -0800690 if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {
San Mehata5078592010-03-25 09:36:54 -0700691 Slog.w(TAG, String.format("Insertion mount failed (%d)", rc));
San Mehat4270e1e2010-01-29 05:32:19 -0800692 }
693 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700694 Slog.w(TAG, "Failed to mount media on insertion", ex);
San Mehat4270e1e2010-01-29 05:32:19 -0800695 }
696 }
697 }.start();
698 } else if (code == VoldResponseCode.VolumeDiskRemoved) {
699 /*
700 * This event gets trumped if we're already in BAD_REMOVAL state
701 */
702 if (getVolumeState(path).equals(Environment.MEDIA_BAD_REMOVAL)) {
703 return true;
704 }
705 /* Send the media unmounted event first */
San Mehata5078592010-03-25 09:36:54 -0700706 if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
San Mehat4270e1e2010-01-29 05:32:19 -0800707 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
708 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
709 mContext.sendBroadcast(in);
710
San Mehata5078592010-03-25 09:36:54 -0700711 if (DEBUG_EVENTS) Slog.i(TAG, "Sending media removed");
San Mehat4270e1e2010-01-29 05:32:19 -0800712 updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
713 in = new Intent(Intent.ACTION_MEDIA_REMOVED, Uri.parse("file://" + path));
714 } else if (code == VoldResponseCode.VolumeBadRemoval) {
San Mehata5078592010-03-25 09:36:54 -0700715 if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
San Mehat4270e1e2010-01-29 05:32:19 -0800716 /* Send the media unmounted event first */
717 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
718 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
719 mContext.sendBroadcast(in);
720
San Mehata5078592010-03-25 09:36:54 -0700721 if (DEBUG_EVENTS) Slog.i(TAG, "Sending media bad removal");
San Mehat4270e1e2010-01-29 05:32:19 -0800722 updatePublicVolumeState(path, Environment.MEDIA_BAD_REMOVAL);
723 in = new Intent(Intent.ACTION_MEDIA_BAD_REMOVAL, Uri.parse("file://" + path));
724 } else {
San Mehata5078592010-03-25 09:36:54 -0700725 Slog.e(TAG, String.format("Unknown code {%d}", code));
San Mehat4270e1e2010-01-29 05:32:19 -0800726 }
727 } else {
728 return false;
729 }
730
731 if (in != null) {
732 mContext.sendBroadcast(in);
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400733 }
734 return true;
San Mehat4270e1e2010-01-29 05:32:19 -0800735 }
736
San Mehat207e5382010-02-04 20:46:54 -0800737 private void notifyVolumeStateChange(String label, String path, int oldState, int newState) {
San Mehat4270e1e2010-01-29 05:32:19 -0800738 String vs = getVolumeState(path);
San Mehata5078592010-03-25 09:36:54 -0700739 if (DEBUG_EVENTS) Slog.i(TAG, "notifyVolumeStateChanged::" + vs);
San Mehat4270e1e2010-01-29 05:32:19 -0800740
741 Intent in = null;
742
Mike Lockwoodbf2dd442010-03-03 06:16:52 -0500743 if (oldState == VolumeState.Shared && newState != oldState) {
San Mehata5078592010-03-25 09:36:54 -0700744 if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_UNSHARED intent");
Mike Lockwoodbf2dd442010-03-03 06:16:52 -0500745 mContext.sendBroadcast(new Intent(Intent.ACTION_MEDIA_UNSHARED,
746 Uri.parse("file://" + path)));
747 }
748
San Mehat4270e1e2010-01-29 05:32:19 -0800749 if (newState == VolumeState.Init) {
750 } else if (newState == VolumeState.NoMedia) {
751 // NoMedia is handled via Disk Remove events
752 } else if (newState == VolumeState.Idle) {
753 /*
754 * Don't notify if we're in BAD_REMOVAL, NOFS, UNMOUNTABLE, or
755 * if we're in the process of enabling UMS
756 */
757 if (!vs.equals(
758 Environment.MEDIA_BAD_REMOVAL) && !vs.equals(
759 Environment.MEDIA_NOFS) && !vs.equals(
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800760 Environment.MEDIA_UNMOUNTABLE) && !getUmsEnabling()) {
San Mehata5078592010-03-25 09:36:54 -0700761 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state for media bad removal nofs and unmountable");
San Mehat4270e1e2010-01-29 05:32:19 -0800762 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
763 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
764 }
765 } else if (newState == VolumeState.Pending) {
766 } else if (newState == VolumeState.Checking) {
San Mehata5078592010-03-25 09:36:54 -0700767 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state checking");
San Mehat4270e1e2010-01-29 05:32:19 -0800768 updatePublicVolumeState(path, Environment.MEDIA_CHECKING);
769 in = new Intent(Intent.ACTION_MEDIA_CHECKING, Uri.parse("file://" + path));
770 } else if (newState == VolumeState.Mounted) {
San Mehata5078592010-03-25 09:36:54 -0700771 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state mounted");
San Mehat4270e1e2010-01-29 05:32:19 -0800772 updatePublicVolumeState(path, Environment.MEDIA_MOUNTED);
San Mehat4270e1e2010-01-29 05:32:19 -0800773 in = new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + path));
774 in.putExtra("read-only", false);
775 } else if (newState == VolumeState.Unmounting) {
San Mehat4270e1e2010-01-29 05:32:19 -0800776 in = new Intent(Intent.ACTION_MEDIA_EJECT, Uri.parse("file://" + path));
777 } else if (newState == VolumeState.Formatting) {
778 } else if (newState == VolumeState.Shared) {
San Mehata5078592010-03-25 09:36:54 -0700779 if (DEBUG_EVENTS) Slog.i(TAG, "Updating volume state media mounted");
San Mehat4270e1e2010-01-29 05:32:19 -0800780 /* Send the media unmounted event first */
781 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
782 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
783 mContext.sendBroadcast(in);
784
San Mehata5078592010-03-25 09:36:54 -0700785 if (DEBUG_EVENTS) Slog.i(TAG, "Updating media shared");
San Mehat4270e1e2010-01-29 05:32:19 -0800786 updatePublicVolumeState(path, Environment.MEDIA_SHARED);
787 in = new Intent(Intent.ACTION_MEDIA_SHARED, Uri.parse("file://" + path));
San Mehata5078592010-03-25 09:36:54 -0700788 if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_SHARED intent");
San Mehat4270e1e2010-01-29 05:32:19 -0800789 } else if (newState == VolumeState.SharedMnt) {
San Mehata5078592010-03-25 09:36:54 -0700790 Slog.e(TAG, "Live shared mounts not supported yet!");
San Mehat4270e1e2010-01-29 05:32:19 -0800791 return;
792 } else {
San Mehata5078592010-03-25 09:36:54 -0700793 Slog.e(TAG, "Unhandled VolumeState {" + newState + "}");
San Mehat4270e1e2010-01-29 05:32:19 -0800794 }
795
796 if (in != null) {
797 mContext.sendBroadcast(in);
798 }
799 }
800
San Mehat207e5382010-02-04 20:46:54 -0800801 private boolean doGetShareMethodAvailable(String method) {
Kenny Root85fb2062010-06-01 20:50:21 -0700802 ArrayList<String> rsp;
Kenny Roota80ce062010-06-01 13:23:53 -0700803 try {
Kenny Root85fb2062010-06-01 20:50:21 -0700804 rsp = mConnector.doCommand("share status " + method);
Kenny Roota80ce062010-06-01 13:23:53 -0700805 } catch (NativeDaemonConnectorException ex) {
806 Slog.e(TAG, "Failed to determine whether share method " + method + " is available.");
807 return false;
808 }
San Mehat207e5382010-02-04 20:46:54 -0800809
810 for (String line : rsp) {
Kenny Roota80ce062010-06-01 13:23:53 -0700811 String[] tok = line.split(" ");
812 if (tok.length < 3) {
813 Slog.e(TAG, "Malformed response to share status " + method);
814 return false;
815 }
816
San Mehat207e5382010-02-04 20:46:54 -0800817 int code;
818 try {
819 code = Integer.parseInt(tok[0]);
820 } catch (NumberFormatException nfe) {
San Mehata5078592010-03-25 09:36:54 -0700821 Slog.e(TAG, String.format("Error parsing code %s", tok[0]));
San Mehat207e5382010-02-04 20:46:54 -0800822 return false;
823 }
824 if (code == VoldResponseCode.ShareStatusResult) {
825 if (tok[2].equals("available"))
826 return true;
827 return false;
828 } else {
San Mehata5078592010-03-25 09:36:54 -0700829 Slog.e(TAG, String.format("Unexpected response code %d", code));
San Mehat207e5382010-02-04 20:46:54 -0800830 return false;
831 }
832 }
San Mehata5078592010-03-25 09:36:54 -0700833 Slog.e(TAG, "Got an empty response");
San Mehat207e5382010-02-04 20:46:54 -0800834 return false;
835 }
836
837 private int doMountVolume(String path) {
San Mehatb1043402010-02-05 08:26:50 -0800838 int rc = StorageResultCode.OperationSucceeded;
San Mehat207e5382010-02-04 20:46:54 -0800839
San Mehata5078592010-03-25 09:36:54 -0700840 if (DEBUG_EVENTS) Slog.i(TAG, "doMountVolume: Mouting " + path);
San Mehat207e5382010-02-04 20:46:54 -0800841 try {
842 mConnector.doCommand(String.format("volume mount %s", path));
843 } catch (NativeDaemonConnectorException e) {
844 /*
845 * Mount failed for some reason
846 */
847 Intent in = null;
848 int code = e.getCode();
849 if (code == VoldResponseCode.OpFailedNoMedia) {
850 /*
851 * Attempt to mount but no media inserted
852 */
San Mehatb1043402010-02-05 08:26:50 -0800853 rc = StorageResultCode.OperationFailedNoMedia;
San Mehat207e5382010-02-04 20:46:54 -0800854 } else if (code == VoldResponseCode.OpFailedMediaBlank) {
San Mehata5078592010-03-25 09:36:54 -0700855 if (DEBUG_EVENTS) Slog.i(TAG, " updating volume state :: media nofs");
San Mehat207e5382010-02-04 20:46:54 -0800856 /*
857 * Media is blank or does not contain a supported filesystem
858 */
859 updatePublicVolumeState(path, Environment.MEDIA_NOFS);
860 in = new Intent(Intent.ACTION_MEDIA_NOFS, Uri.parse("file://" + path));
San Mehatb1043402010-02-05 08:26:50 -0800861 rc = StorageResultCode.OperationFailedMediaBlank;
San Mehat207e5382010-02-04 20:46:54 -0800862 } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
San Mehata5078592010-03-25 09:36:54 -0700863 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state media corrupt");
San Mehat207e5382010-02-04 20:46:54 -0800864 /*
865 * Volume consistency check failed
866 */
867 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTABLE);
868 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE, Uri.parse("file://" + path));
San Mehatb1043402010-02-05 08:26:50 -0800869 rc = StorageResultCode.OperationFailedMediaCorrupt;
San Mehat207e5382010-02-04 20:46:54 -0800870 } else {
San Mehatb1043402010-02-05 08:26:50 -0800871 rc = StorageResultCode.OperationFailedInternalError;
San Mehat207e5382010-02-04 20:46:54 -0800872 }
873
874 /*
875 * Send broadcast intent (if required for the failure)
876 */
877 if (in != null) {
878 mContext.sendBroadcast(in);
879 }
880 }
881
882 return rc;
883 }
884
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800885 /*
886 * If force is not set, we do not unmount if there are
887 * processes holding references to the volume about to be unmounted.
888 * If force is set, all the processes holding references need to be
889 * killed via the ActivityManager before actually unmounting the volume.
890 * This might even take a while and might be retried after timed delays
891 * to make sure we dont end up in an instable state and kill some core
892 * processes.
893 */
San Mehatd9709982010-02-18 11:43:03 -0800894 private int doUnmountVolume(String path, boolean force) {
San Mehat59443a62010-02-09 13:28:45 -0800895 if (!getVolumeState(path).equals(Environment.MEDIA_MOUNTED)) {
San Mehat207e5382010-02-04 20:46:54 -0800896 return VoldResponseCode.OpFailedVolNotMounted;
897 }
Kenny Rootaa485402010-09-14 14:49:41 -0700898
899 /*
900 * Force a GC to make sure AssetManagers in other threads of the
901 * system_server are cleaned up. We have to do this since AssetManager
902 * instances are kept as a WeakReference and it's possible we have files
903 * open on the external storage.
904 */
905 Runtime.getRuntime().gc();
906
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800907 // Redundant probably. But no harm in updating state again.
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700908 mPms.updateExternalMediaStatus(false, false);
San Mehat207e5382010-02-04 20:46:54 -0800909 try {
San Mehatd9709982010-02-18 11:43:03 -0800910 mConnector.doCommand(String.format(
911 "volume unmount %s%s", path, (force ? " force" : "")));
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700912 // We unmounted the volume. None of the asec containers are available now.
913 synchronized (mAsecMountSet) {
914 mAsecMountSet.clear();
915 }
San Mehatb1043402010-02-05 08:26:50 -0800916 return StorageResultCode.OperationSucceeded;
San Mehat207e5382010-02-04 20:46:54 -0800917 } catch (NativeDaemonConnectorException e) {
918 // Don't worry about mismatch in PackageManager since the
919 // call back will handle the status changes any way.
920 int code = e.getCode();
921 if (code == VoldResponseCode.OpFailedVolNotMounted) {
San Mehata181b212010-02-11 06:50:20 -0800922 return StorageResultCode.OperationFailedStorageNotMounted;
San Mehatd9709982010-02-18 11:43:03 -0800923 } else if (code == VoldResponseCode.OpFailedStorageBusy) {
924 return StorageResultCode.OperationFailedStorageBusy;
San Mehat207e5382010-02-04 20:46:54 -0800925 } else {
San Mehatb1043402010-02-05 08:26:50 -0800926 return StorageResultCode.OperationFailedInternalError;
San Mehat207e5382010-02-04 20:46:54 -0800927 }
928 }
929 }
930
931 private int doFormatVolume(String path) {
932 try {
933 String cmd = String.format("volume format %s", path);
934 mConnector.doCommand(cmd);
San Mehatb1043402010-02-05 08:26:50 -0800935 return StorageResultCode.OperationSucceeded;
San Mehat207e5382010-02-04 20:46:54 -0800936 } catch (NativeDaemonConnectorException e) {
937 int code = e.getCode();
938 if (code == VoldResponseCode.OpFailedNoMedia) {
San Mehatb1043402010-02-05 08:26:50 -0800939 return StorageResultCode.OperationFailedNoMedia;
San Mehat207e5382010-02-04 20:46:54 -0800940 } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
San Mehatb1043402010-02-05 08:26:50 -0800941 return StorageResultCode.OperationFailedMediaCorrupt;
San Mehat207e5382010-02-04 20:46:54 -0800942 } else {
San Mehatb1043402010-02-05 08:26:50 -0800943 return StorageResultCode.OperationFailedInternalError;
San Mehat207e5382010-02-04 20:46:54 -0800944 }
945 }
946 }
947
San Mehatb1043402010-02-05 08:26:50 -0800948 private boolean doGetVolumeShared(String path, String method) {
949 String cmd = String.format("volume shared %s %s", path, method);
Kenny Roota80ce062010-06-01 13:23:53 -0700950 ArrayList<String> rsp;
951
952 try {
953 rsp = mConnector.doCommand(cmd);
954 } catch (NativeDaemonConnectorException ex) {
955 Slog.e(TAG, "Failed to read response to volume shared " + path + " " + method);
956 return false;
957 }
San Mehatb1043402010-02-05 08:26:50 -0800958
959 for (String line : rsp) {
Kenny Roota80ce062010-06-01 13:23:53 -0700960 String[] tok = line.split(" ");
961 if (tok.length < 3) {
962 Slog.e(TAG, "Malformed response to volume shared " + path + " " + method + " command");
963 return false;
964 }
965
San Mehatb1043402010-02-05 08:26:50 -0800966 int code;
967 try {
968 code = Integer.parseInt(tok[0]);
969 } catch (NumberFormatException nfe) {
San Mehata5078592010-03-25 09:36:54 -0700970 Slog.e(TAG, String.format("Error parsing code %s", tok[0]));
San Mehatb1043402010-02-05 08:26:50 -0800971 return false;
972 }
973 if (code == VoldResponseCode.ShareEnabledResult) {
Kenny Roota80ce062010-06-01 13:23:53 -0700974 return "enabled".equals(tok[2]);
San Mehatb1043402010-02-05 08:26:50 -0800975 } else {
San Mehata5078592010-03-25 09:36:54 -0700976 Slog.e(TAG, String.format("Unexpected response code %d", code));
San Mehatb1043402010-02-05 08:26:50 -0800977 return false;
978 }
979 }
San Mehata5078592010-03-25 09:36:54 -0700980 Slog.e(TAG, "Got an empty response");
San Mehatb1043402010-02-05 08:26:50 -0800981 return false;
982 }
983
San Mehat207e5382010-02-04 20:46:54 -0800984 private void notifyShareAvailabilityChange(String method, final boolean avail) {
San Mehat4270e1e2010-01-29 05:32:19 -0800985 if (!method.equals("ums")) {
San Mehata5078592010-03-25 09:36:54 -0700986 Slog.w(TAG, "Ignoring unsupported share method {" + method + "}");
San Mehat4270e1e2010-01-29 05:32:19 -0800987 return;
988 }
989
990 synchronized (mListeners) {
991 for (int i = mListeners.size() -1; i >= 0; i--) {
992 MountServiceBinderListener bl = mListeners.get(i);
993 try {
San Mehatb1043402010-02-05 08:26:50 -0800994 bl.mListener.onUsbMassStorageConnectionChanged(avail);
San Mehat4270e1e2010-01-29 05:32:19 -0800995 } catch (RemoteException rex) {
San Mehata5078592010-03-25 09:36:54 -0700996 Slog.e(TAG, "Listener dead");
San Mehat4270e1e2010-01-29 05:32:19 -0800997 mListeners.remove(i);
998 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700999 Slog.e(TAG, "Listener failed", ex);
San Mehat4270e1e2010-01-29 05:32:19 -08001000 }
1001 }
1002 }
1003
San Mehat207e5382010-02-04 20:46:54 -08001004 if (mBooted == true) {
San Mehat6a965af22010-02-24 17:47:30 -08001005 sendUmsIntent(avail);
1006 } else {
1007 mSendUmsConnectedOnBoot = avail;
San Mehat4270e1e2010-01-29 05:32:19 -08001008 }
San Mehat2fe718a2010-03-11 12:01:49 -08001009
1010 final String path = Environment.getExternalStorageDirectory().getPath();
1011 if (avail == false && getVolumeState(path).equals(Environment.MEDIA_SHARED)) {
1012 /*
1013 * USB mass storage disconnected while enabled
1014 */
1015 new Thread() {
Jason parks5af0b912010-11-29 09:05:25 -06001016 @Override
San Mehat2fe718a2010-03-11 12:01:49 -08001017 public void run() {
1018 try {
1019 int rc;
San Mehata5078592010-03-25 09:36:54 -07001020 Slog.w(TAG, "Disabling UMS after cable disconnect");
San Mehat2fe718a2010-03-11 12:01:49 -08001021 doShareUnshareVolume(path, "ums", false);
1022 if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {
San Mehata5078592010-03-25 09:36:54 -07001023 Slog.e(TAG, String.format(
San Mehat2fe718a2010-03-11 12:01:49 -08001024 "Failed to remount {%s} on UMS enabled-disconnect (%d)",
1025 path, rc));
1026 }
1027 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -07001028 Slog.w(TAG, "Failed to mount media on UMS enabled-disconnect", ex);
San Mehat2fe718a2010-03-11 12:01:49 -08001029 }
1030 }
1031 }.start();
1032 }
San Mehat4270e1e2010-01-29 05:32:19 -08001033 }
1034
San Mehat6a965af22010-02-24 17:47:30 -08001035 private void sendUmsIntent(boolean c) {
1036 mContext.sendBroadcast(
1037 new Intent((c ? Intent.ACTION_UMS_CONNECTED : Intent.ACTION_UMS_DISCONNECTED)));
1038 }
1039
San Mehat207e5382010-02-04 20:46:54 -08001040 private void validatePermission(String perm) {
San Mehat4270e1e2010-01-29 05:32:19 -08001041 if (mContext.checkCallingOrSelfPermission(perm) != PackageManager.PERMISSION_GRANTED) {
1042 throw new SecurityException(String.format("Requires %s permission", perm));
1043 }
1044 }
1045
1046 /**
San Mehat207e5382010-02-04 20:46:54 -08001047 * Constructs a new MountService instance
1048 *
1049 * @param context Binder context for this service
1050 */
1051 public MountService(Context context) {
1052 mContext = context;
1053
Mike Lockwood03559752010-07-19 18:25:03 -04001054 mEmulateExternalStorage = context.getResources().getBoolean(
1055 com.android.internal.R.bool.config_emulateExternalStorage);
1056 if (mEmulateExternalStorage) {
1057 Slog.d(TAG, "using emulated external storage");
1058 mLegacyState = Environment.MEDIA_MOUNTED;
1059 }
1060
San Mehat207e5382010-02-04 20:46:54 -08001061 // XXX: This will go away soon in favor of IMountServiceObserver
1062 mPms = (PackageManagerService) ServiceManager.getService("package");
1063
1064 mContext.registerReceiver(mBroadcastReceiver,
1065 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
1066
Daniel Sandler5f27ef42010-03-16 15:42:02 -04001067 mHandlerThread = new HandlerThread("MountService");
1068 mHandlerThread.start();
1069 mHandler = new MountServiceHandler(mHandlerThread.getLooper());
1070
Kenny Roota02b8b02010-08-05 16:14:17 -07001071 // Add OBB Action Handler to MountService thread.
1072 mObbActionHandler = new ObbActionHandler(mHandlerThread.getLooper());
1073
Marco Nelissenc34ebce2010-02-18 13:39:41 -08001074 /*
1075 * Vold does not run in the simulator, so pretend the connector thread
1076 * ran and did its thing.
1077 */
1078 if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
1079 mReady = true;
1080 mUmsEnabling = true;
1081 return;
1082 }
1083
Kenny Root305bcbf2010-09-03 07:56:38 -07001084 /*
1085 * Create the connection to vold with a maximum queue of twice the
1086 * amount of containers we'd ever expect to have. This keeps an
1087 * "asec list" from blocking a thread repeatedly.
1088 */
1089 mConnector = new NativeDaemonConnector(this, "vold",
1090 PackageManagerService.MAX_CONTAINERS * 2, VOLD_TAG);
San Mehat207e5382010-02-04 20:46:54 -08001091 mReady = false;
Kenny Root305bcbf2010-09-03 07:56:38 -07001092 Thread thread = new Thread(mConnector, VOLD_TAG);
San Mehat207e5382010-02-04 20:46:54 -08001093 thread.start();
1094 }
1095
1096 /**
San Mehat4270e1e2010-01-29 05:32:19 -08001097 * Exposed API calls below here
1098 */
1099
1100 public void registerListener(IMountServiceListener listener) {
1101 synchronized (mListeners) {
1102 MountServiceBinderListener bl = new MountServiceBinderListener(listener);
1103 try {
1104 listener.asBinder().linkToDeath(bl, 0);
1105 mListeners.add(bl);
1106 } catch (RemoteException rex) {
San Mehata5078592010-03-25 09:36:54 -07001107 Slog.e(TAG, "Failed to link to listener death");
San Mehat4270e1e2010-01-29 05:32:19 -08001108 }
1109 }
1110 }
1111
1112 public void unregisterListener(IMountServiceListener listener) {
1113 synchronized (mListeners) {
1114 for(MountServiceBinderListener bl : mListeners) {
1115 if (bl.mListener == listener) {
1116 mListeners.remove(mListeners.indexOf(bl));
1117 return;
1118 }
1119 }
1120 }
1121 }
1122
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -08001123 public void shutdown(final IMountShutdownObserver observer) {
San Mehat4270e1e2010-01-29 05:32:19 -08001124 validatePermission(android.Manifest.permission.SHUTDOWN);
1125
San Mehata5078592010-03-25 09:36:54 -07001126 Slog.i(TAG, "Shutting down");
San Mehat4270e1e2010-01-29 05:32:19 -08001127
1128 String path = Environment.getExternalStorageDirectory().getPath();
1129 String state = getVolumeState(path);
San Mehat91c77612010-01-07 10:39:41 -08001130
1131 if (state.equals(Environment.MEDIA_SHARED)) {
1132 /*
1133 * If the media is currently shared, unshare it.
1134 * XXX: This is still dangerous!. We should not
1135 * be rebooting at *all* if UMS is enabled, since
1136 * the UMS host could have dirty FAT cache entries
1137 * yet to flush.
1138 */
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001139 setUsbMassStorageEnabled(false);
San Mehat91c77612010-01-07 10:39:41 -08001140 } else if (state.equals(Environment.MEDIA_CHECKING)) {
1141 /*
1142 * If the media is being checked, then we need to wait for
1143 * it to complete before being able to proceed.
1144 */
1145 // XXX: @hackbod - Should we disable the ANR timer here?
1146 int retries = 30;
1147 while (state.equals(Environment.MEDIA_CHECKING) && (retries-- >=0)) {
1148 try {
1149 Thread.sleep(1000);
1150 } catch (InterruptedException iex) {
San Mehata5078592010-03-25 09:36:54 -07001151 Slog.e(TAG, "Interrupted while waiting for media", iex);
San Mehat91c77612010-01-07 10:39:41 -08001152 break;
1153 }
1154 state = Environment.getExternalStorageState();
1155 }
1156 if (retries == 0) {
San Mehata5078592010-03-25 09:36:54 -07001157 Slog.e(TAG, "Timed out waiting for media to check");
San Mehat91c77612010-01-07 10:39:41 -08001158 }
1159 }
1160
1161 if (state.equals(Environment.MEDIA_MOUNTED)) {
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -08001162 // Post a unmount message.
1163 ShutdownCallBack ucb = new ShutdownCallBack(path, observer);
1164 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
Johan Alfven5d0db4d2010-11-09 10:32:25 +01001165 } else if (observer != null) {
1166 /*
1167 * Observer is waiting for onShutDownComplete when we are done.
1168 * Since nothing will be done send notification directly so shutdown
1169 * sequence can continue.
1170 */
1171 try {
1172 observer.onShutDownComplete(StorageResultCode.OperationSucceeded);
1173 } catch (RemoteException e) {
1174 Slog.w(TAG, "RemoteException when shutting down");
1175 }
San Mehat4270e1e2010-01-29 05:32:19 -08001176 }
1177 }
1178
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001179 private boolean getUmsEnabling() {
1180 synchronized (mListeners) {
1181 return mUmsEnabling;
1182 }
1183 }
1184
1185 private void setUmsEnabling(boolean enable) {
1186 synchronized (mListeners) {
Tony Wufc711252010-08-09 16:49:19 +08001187 mUmsEnabling = enable;
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001188 }
1189 }
1190
San Mehatb1043402010-02-05 08:26:50 -08001191 public boolean isUsbMassStorageConnected() {
San Mehat207e5382010-02-04 20:46:54 -08001192 waitForReady();
San Mehat91c77612010-01-07 10:39:41 -08001193
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001194 if (getUmsEnabling()) {
San Mehatb1043402010-02-05 08:26:50 -08001195 return true;
San Mehat7fd0fee2009-12-17 07:12:23 -08001196 }
San Mehatb1043402010-02-05 08:26:50 -08001197 return doGetShareMethodAvailable("ums");
1198 }
1199
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001200 public void setUsbMassStorageEnabled(boolean enable) {
San Mehatb1043402010-02-05 08:26:50 -08001201 waitForReady();
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001202 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
San Mehatb1043402010-02-05 08:26:50 -08001203
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001204 // TODO: Add support for multiple share methods
1205
1206 /*
1207 * If the volume is mounted and we're enabling then unmount it
1208 */
1209 String path = Environment.getExternalStorageDirectory().getPath();
1210 String vs = getVolumeState(path);
1211 String method = "ums";
1212 if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
1213 // Override for isUsbMassStorageEnabled()
1214 setUmsEnabling(enable);
1215 UmsEnableCallBack umscb = new UmsEnableCallBack(path, method, true);
1216 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, umscb));
1217 // Clear override
1218 setUmsEnabling(false);
1219 }
1220 /*
1221 * If we disabled UMS then mount the volume
1222 */
1223 if (!enable) {
1224 doShareUnshareVolume(path, method, enable);
1225 if (doMountVolume(path) != StorageResultCode.OperationSucceeded) {
San Mehata5078592010-03-25 09:36:54 -07001226 Slog.e(TAG, "Failed to remount " + path +
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001227 " after disabling share method " + method);
1228 /*
1229 * Even though the mount failed, the unshare didn't so don't indicate an error.
1230 * The mountVolume() call will have set the storage state and sent the necessary
1231 * broadcasts.
1232 */
1233 }
1234 }
San Mehatb1043402010-02-05 08:26:50 -08001235 }
1236
1237 public boolean isUsbMassStorageEnabled() {
1238 waitForReady();
1239 return doGetVolumeShared(Environment.getExternalStorageDirectory().getPath(), "ums");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001240 }
Jason parks9ed98bc2011-01-17 09:58:35 -06001241
San Mehat7fd0fee2009-12-17 07:12:23 -08001242 /**
1243 * @return state of the volume at the specified mount point
1244 */
San Mehat4270e1e2010-01-29 05:32:19 -08001245 public String getVolumeState(String mountPoint) {
San Mehat7fd0fee2009-12-17 07:12:23 -08001246 /*
1247 * XXX: Until we have multiple volume discovery, just hardwire
1248 * this to /sdcard
1249 */
1250 if (!mountPoint.equals(Environment.getExternalStorageDirectory().getPath())) {
San Mehata5078592010-03-25 09:36:54 -07001251 Slog.w(TAG, "getVolumeState(" + mountPoint + "): Unknown volume");
San Mehat7fd0fee2009-12-17 07:12:23 -08001252 throw new IllegalArgumentException();
1253 }
1254
1255 return mLegacyState;
1256 }
1257
Kenny Roote1ff2142010-10-12 11:20:01 -07001258 public boolean isExternalStorageEmulated() {
1259 return mEmulateExternalStorage;
1260 }
1261
San Mehat4270e1e2010-01-29 05:32:19 -08001262 public int mountVolume(String path) {
1263 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
San Mehat4270e1e2010-01-29 05:32:19 -08001264
San Mehat207e5382010-02-04 20:46:54 -08001265 waitForReady();
1266 return doMountVolume(path);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001267 }
1268
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -08001269 public void unmountVolume(String path, boolean force) {
San Mehat4270e1e2010-01-29 05:32:19 -08001270 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
San Mehat207e5382010-02-04 20:46:54 -08001271 waitForReady();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001272
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -08001273 String volState = getVolumeState(path);
San Mehata5078592010-03-25 09:36:54 -07001274 if (DEBUG_UNMOUNT) Slog.i(TAG, "Unmounting " + path + " force = " + force);
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -08001275 if (Environment.MEDIA_UNMOUNTED.equals(volState) ||
1276 Environment.MEDIA_REMOVED.equals(volState) ||
1277 Environment.MEDIA_SHARED.equals(volState) ||
1278 Environment.MEDIA_UNMOUNTABLE.equals(volState)) {
1279 // Media already unmounted or cannot be unmounted.
1280 // TODO return valid return code when adding observer call back.
1281 return;
1282 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -08001283 UnmountCallBack ucb = new UnmountCallBack(path, force);
1284 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001285 }
1286
San Mehat4270e1e2010-01-29 05:32:19 -08001287 public int formatVolume(String path) {
1288 validatePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
San Mehat207e5382010-02-04 20:46:54 -08001289 waitForReady();
San Mehat5b77dab2010-01-26 13:28:50 -08001290
San Mehat207e5382010-02-04 20:46:54 -08001291 return doFormatVolume(path);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001292 }
1293
San Mehatc1b4ce92010-02-16 17:13:03 -08001294 public int []getStorageUsers(String path) {
1295 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1296 waitForReady();
1297 try {
1298 String[] r = mConnector.doListCommand(
1299 String.format("storage users %s", path),
1300 VoldResponseCode.StorageUsersListResult);
1301 // FMT: <pid> <process name>
1302 int[] data = new int[r.length];
1303 for (int i = 0; i < r.length; i++) {
1304 String []tok = r[i].split(" ");
1305 try {
1306 data[i] = Integer.parseInt(tok[0]);
1307 } catch (NumberFormatException nfe) {
San Mehata5078592010-03-25 09:36:54 -07001308 Slog.e(TAG, String.format("Error parsing pid %s", tok[0]));
San Mehatc1b4ce92010-02-16 17:13:03 -08001309 return new int[0];
1310 }
1311 }
1312 return data;
1313 } catch (NativeDaemonConnectorException e) {
San Mehata5078592010-03-25 09:36:54 -07001314 Slog.e(TAG, "Failed to retrieve storage users list", e);
San Mehatc1b4ce92010-02-16 17:13:03 -08001315 return new int[0];
1316 }
1317 }
1318
San Mehatb1043402010-02-05 08:26:50 -08001319 private void warnOnNotMounted() {
1320 if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
San Mehata5078592010-03-25 09:36:54 -07001321 Slog.w(TAG, "getSecureContainerList() called when storage not mounted");
San Mehatb1043402010-02-05 08:26:50 -08001322 }
1323 }
1324
San Mehat4270e1e2010-01-29 05:32:19 -08001325 public String[] getSecureContainerList() {
1326 validatePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat207e5382010-02-04 20:46:54 -08001327 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001328 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001329
San Mehat4270e1e2010-01-29 05:32:19 -08001330 try {
1331 return mConnector.doListCommand("asec list", VoldResponseCode.AsecListResult);
1332 } catch (NativeDaemonConnectorException e) {
1333 return new String[0];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001334 }
1335 }
San Mehat36972292010-01-06 11:06:32 -08001336
San Mehat4270e1e2010-01-29 05:32:19 -08001337 public int createSecureContainer(String id, int sizeMb, String fstype,
1338 String key, int ownerUid) {
1339 validatePermission(android.Manifest.permission.ASEC_CREATE);
San Mehat207e5382010-02-04 20:46:54 -08001340 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001341 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001342
San Mehatb1043402010-02-05 08:26:50 -08001343 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001344 String cmd = String.format("asec create %s %d %s %s %d", id, sizeMb, fstype, key, ownerUid);
1345 try {
1346 mConnector.doCommand(cmd);
1347 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001348 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001349 }
San Mehata181b212010-02-11 06:50:20 -08001350
1351 if (rc == StorageResultCode.OperationSucceeded) {
1352 synchronized (mAsecMountSet) {
1353 mAsecMountSet.add(id);
1354 }
1355 }
San Mehat4270e1e2010-01-29 05:32:19 -08001356 return rc;
San Mehat36972292010-01-06 11:06:32 -08001357 }
1358
San Mehat4270e1e2010-01-29 05:32:19 -08001359 public int finalizeSecureContainer(String id) {
1360 validatePermission(android.Manifest.permission.ASEC_CREATE);
San Mehatb1043402010-02-05 08:26:50 -08001361 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001362
San Mehatb1043402010-02-05 08:26:50 -08001363 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001364 try {
1365 mConnector.doCommand(String.format("asec finalize %s", id));
San Mehata181b212010-02-11 06:50:20 -08001366 /*
1367 * Finalization does a remount, so no need
1368 * to update mAsecMountSet
1369 */
San Mehat4270e1e2010-01-29 05:32:19 -08001370 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001371 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001372 }
San Mehat4270e1e2010-01-29 05:32:19 -08001373 return rc;
San Mehat36972292010-01-06 11:06:32 -08001374 }
1375
San Mehatd9709982010-02-18 11:43:03 -08001376 public int destroySecureContainer(String id, boolean force) {
San Mehat4270e1e2010-01-29 05:32:19 -08001377 validatePermission(android.Manifest.permission.ASEC_DESTROY);
San Mehat207e5382010-02-04 20:46:54 -08001378 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001379 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001380
Kenny Rootaa485402010-09-14 14:49:41 -07001381 /*
1382 * Force a GC to make sure AssetManagers in other threads of the
1383 * system_server are cleaned up. We have to do this since AssetManager
1384 * instances are kept as a WeakReference and it's possible we have files
1385 * open on the external storage.
1386 */
1387 Runtime.getRuntime().gc();
1388
San Mehatb1043402010-02-05 08:26:50 -08001389 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001390 try {
San Mehatd9709982010-02-18 11:43:03 -08001391 mConnector.doCommand(String.format("asec destroy %s%s", id, (force ? " force" : "")));
San Mehat4270e1e2010-01-29 05:32:19 -08001392 } catch (NativeDaemonConnectorException e) {
San Mehatd9709982010-02-18 11:43:03 -08001393 int code = e.getCode();
1394 if (code == VoldResponseCode.OpFailedStorageBusy) {
1395 rc = StorageResultCode.OperationFailedStorageBusy;
1396 } else {
1397 rc = StorageResultCode.OperationFailedInternalError;
1398 }
San Mehat02735bc2010-01-26 15:18:08 -08001399 }
San Mehata181b212010-02-11 06:50:20 -08001400
1401 if (rc == StorageResultCode.OperationSucceeded) {
1402 synchronized (mAsecMountSet) {
1403 if (mAsecMountSet.contains(id)) {
1404 mAsecMountSet.remove(id);
1405 }
1406 }
1407 }
1408
San Mehat4270e1e2010-01-29 05:32:19 -08001409 return rc;
San Mehat36972292010-01-06 11:06:32 -08001410 }
Jason parks9ed98bc2011-01-17 09:58:35 -06001411
San Mehat4270e1e2010-01-29 05:32:19 -08001412 public int mountSecureContainer(String id, String key, int ownerUid) {
1413 validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
San Mehat207e5382010-02-04 20:46:54 -08001414 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001415 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001416
San Mehata181b212010-02-11 06:50:20 -08001417 synchronized (mAsecMountSet) {
1418 if (mAsecMountSet.contains(id)) {
1419 return StorageResultCode.OperationFailedStorageMounted;
1420 }
1421 }
1422
San Mehatb1043402010-02-05 08:26:50 -08001423 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001424 String cmd = String.format("asec mount %s %s %d", id, key, ownerUid);
1425 try {
1426 mConnector.doCommand(cmd);
1427 } catch (NativeDaemonConnectorException e) {
Kenny Rootf0304622010-03-19 19:20:42 -07001428 int code = e.getCode();
1429 if (code != VoldResponseCode.OpFailedStorageBusy) {
1430 rc = StorageResultCode.OperationFailedInternalError;
1431 }
San Mehat02735bc2010-01-26 15:18:08 -08001432 }
San Mehat6cdd9c02010-02-09 14:45:20 -08001433
1434 if (rc == StorageResultCode.OperationSucceeded) {
1435 synchronized (mAsecMountSet) {
1436 mAsecMountSet.add(id);
1437 }
1438 }
San Mehat4270e1e2010-01-29 05:32:19 -08001439 return rc;
San Mehat36972292010-01-06 11:06:32 -08001440 }
1441
San Mehatd9709982010-02-18 11:43:03 -08001442 public int unmountSecureContainer(String id, boolean force) {
San Mehat4270e1e2010-01-29 05:32:19 -08001443 validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
San Mehat207e5382010-02-04 20:46:54 -08001444 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001445 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001446
San Mehat6cdd9c02010-02-09 14:45:20 -08001447 synchronized (mAsecMountSet) {
1448 if (!mAsecMountSet.contains(id)) {
San Mehata181b212010-02-11 06:50:20 -08001449 return StorageResultCode.OperationFailedStorageNotMounted;
San Mehat6cdd9c02010-02-09 14:45:20 -08001450 }
1451 }
1452
Kenny Rootaa485402010-09-14 14:49:41 -07001453 /*
1454 * Force a GC to make sure AssetManagers in other threads of the
1455 * system_server are cleaned up. We have to do this since AssetManager
1456 * instances are kept as a WeakReference and it's possible we have files
1457 * open on the external storage.
1458 */
1459 Runtime.getRuntime().gc();
1460
San Mehatb1043402010-02-05 08:26:50 -08001461 int rc = StorageResultCode.OperationSucceeded;
San Mehatd9709982010-02-18 11:43:03 -08001462 String cmd = String.format("asec unmount %s%s", id, (force ? " force" : ""));
San Mehat4270e1e2010-01-29 05:32:19 -08001463 try {
1464 mConnector.doCommand(cmd);
1465 } catch (NativeDaemonConnectorException e) {
San Mehatd9709982010-02-18 11:43:03 -08001466 int code = e.getCode();
1467 if (code == VoldResponseCode.OpFailedStorageBusy) {
1468 rc = StorageResultCode.OperationFailedStorageBusy;
1469 } else {
1470 rc = StorageResultCode.OperationFailedInternalError;
1471 }
San Mehat02735bc2010-01-26 15:18:08 -08001472 }
San Mehat6cdd9c02010-02-09 14:45:20 -08001473
1474 if (rc == StorageResultCode.OperationSucceeded) {
1475 synchronized (mAsecMountSet) {
1476 mAsecMountSet.remove(id);
1477 }
1478 }
San Mehat4270e1e2010-01-29 05:32:19 -08001479 return rc;
San Mehat9dba7092010-01-18 06:47:41 -08001480 }
1481
San Mehat6cdd9c02010-02-09 14:45:20 -08001482 public boolean isSecureContainerMounted(String id) {
1483 validatePermission(android.Manifest.permission.ASEC_ACCESS);
1484 waitForReady();
1485 warnOnNotMounted();
1486
1487 synchronized (mAsecMountSet) {
1488 return mAsecMountSet.contains(id);
1489 }
1490 }
1491
San Mehat4270e1e2010-01-29 05:32:19 -08001492 public int renameSecureContainer(String oldId, String newId) {
1493 validatePermission(android.Manifest.permission.ASEC_RENAME);
San Mehat207e5382010-02-04 20:46:54 -08001494 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001495 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001496
San Mehata181b212010-02-11 06:50:20 -08001497 synchronized (mAsecMountSet) {
San Mehat85451ee2010-02-24 08:54:18 -08001498 /*
Jason parks9ed98bc2011-01-17 09:58:35 -06001499 * Because a mounted container has active internal state which cannot be
San Mehat85451ee2010-02-24 08:54:18 -08001500 * changed while active, we must ensure both ids are not currently mounted.
1501 */
1502 if (mAsecMountSet.contains(oldId) || mAsecMountSet.contains(newId)) {
San Mehata181b212010-02-11 06:50:20 -08001503 return StorageResultCode.OperationFailedStorageMounted;
1504 }
1505 }
1506
San Mehatb1043402010-02-05 08:26:50 -08001507 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001508 String cmd = String.format("asec rename %s %s", oldId, newId);
1509 try {
1510 mConnector.doCommand(cmd);
1511 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001512 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001513 }
San Mehata181b212010-02-11 06:50:20 -08001514
San Mehat4270e1e2010-01-29 05:32:19 -08001515 return rc;
San Mehat45f61042010-01-23 08:12:43 -08001516 }
1517
San Mehat4270e1e2010-01-29 05:32:19 -08001518 public String getSecureContainerPath(String id) {
1519 validatePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat207e5382010-02-04 20:46:54 -08001520 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001521 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001522
San Mehat2d66cef2010-03-23 11:12:52 -07001523 try {
1524 ArrayList<String> rsp = mConnector.doCommand(String.format("asec path %s", id));
1525 String []tok = rsp.get(0).split(" ");
San Mehat22dd86e2010-01-12 12:21:18 -08001526 int code = Integer.parseInt(tok[0]);
San Mehat2d66cef2010-03-23 11:12:52 -07001527 if (code != VoldResponseCode.AsecPathResult) {
1528 throw new IllegalStateException(String.format("Unexpected response code %d", code));
1529 }
1530 return tok[1];
1531 } catch (NativeDaemonConnectorException e) {
1532 int code = e.getCode();
1533 if (code == VoldResponseCode.OpFailedStorageNotFound) {
1534 throw new IllegalArgumentException(String.format("Container '%s' not found", id));
San Mehat22dd86e2010-01-12 12:21:18 -08001535 } else {
San Mehat2d66cef2010-03-23 11:12:52 -07001536 throw new IllegalStateException(String.format("Unexpected response code %d", code));
San Mehat22dd86e2010-01-12 12:21:18 -08001537 }
1538 }
San Mehat22dd86e2010-01-12 12:21:18 -08001539 }
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -07001540
1541 public void finishMediaUpdate() {
1542 mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
1543 }
Kenny Root02c87302010-07-01 08:10:18 -07001544
Kenny Roota02b8b02010-08-05 16:14:17 -07001545 private boolean isUidOwnerOfPackageOrSystem(String packageName, int callerUid) {
1546 if (callerUid == android.os.Process.SYSTEM_UID) {
1547 return true;
1548 }
1549
Kenny Root02c87302010-07-01 08:10:18 -07001550 if (packageName == null) {
1551 return false;
1552 }
1553
1554 final int packageUid = mPms.getPackageUid(packageName);
1555
1556 if (DEBUG_OBB) {
1557 Slog.d(TAG, "packageName = " + packageName + ", packageUid = " +
1558 packageUid + ", callerUid = " + callerUid);
1559 }
1560
1561 return callerUid == packageUid;
1562 }
1563
1564 public String getMountedObbPath(String filename) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07001565 if (filename == null) {
1566 throw new IllegalArgumentException("filename cannot be null");
1567 }
1568
Kenny Root02c87302010-07-01 08:10:18 -07001569 waitForReady();
1570 warnOnNotMounted();
1571
Kenny Root02c87302010-07-01 08:10:18 -07001572 try {
1573 ArrayList<String> rsp = mConnector.doCommand(String.format("obb path %s", filename));
1574 String []tok = rsp.get(0).split(" ");
1575 int code = Integer.parseInt(tok[0]);
1576 if (code != VoldResponseCode.AsecPathResult) {
1577 throw new IllegalStateException(String.format("Unexpected response code %d", code));
1578 }
1579 return tok[1];
1580 } catch (NativeDaemonConnectorException e) {
1581 int code = e.getCode();
1582 if (code == VoldResponseCode.OpFailedStorageNotFound) {
Kenny Roota02b8b02010-08-05 16:14:17 -07001583 return null;
Kenny Root02c87302010-07-01 08:10:18 -07001584 } else {
1585 throw new IllegalStateException(String.format("Unexpected response code %d", code));
1586 }
1587 }
1588 }
1589
1590 public boolean isObbMounted(String filename) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07001591 if (filename == null) {
1592 throw new IllegalArgumentException("filename cannot be null");
Kenny Root02c87302010-07-01 08:10:18 -07001593 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07001594
1595 synchronized (mObbMounts) {
1596 return mObbPathToStateMap.containsKey(filename);
1597 }
Kenny Root02c87302010-07-01 08:10:18 -07001598 }
1599
Kenny Rootaf9d6672010-10-08 09:21:39 -07001600 public void mountObb(String filename, String key, IObbActionListener token, int nonce)
Kenny Root735de3b2010-09-30 14:11:39 -07001601 throws RemoteException {
Kenny Rootf1121dc2010-09-29 07:30:53 -07001602 if (filename == null) {
1603 throw new IllegalArgumentException("filename cannot be null");
Kenny Rootaf9d6672010-10-08 09:21:39 -07001604 }
1605
1606 if (token == null) {
Kenny Rootf1121dc2010-09-29 07:30:53 -07001607 throw new IllegalArgumentException("token cannot be null");
1608 }
1609
Kenny Rootaf9d6672010-10-08 09:21:39 -07001610 final int callerUid = Binder.getCallingUid();
1611 final ObbState obbState = new ObbState(filename, callerUid, token, nonce);
1612 final ObbAction action = new MountObbAction(obbState, key);
Kenny Roota02b8b02010-08-05 16:14:17 -07001613 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
1614
1615 if (DEBUG_OBB)
1616 Slog.i(TAG, "Send to OBB handler: " + action.toString());
Kenny Root02c87302010-07-01 08:10:18 -07001617 }
1618
Kenny Rootaf9d6672010-10-08 09:21:39 -07001619 public void unmountObb(String filename, boolean force, IObbActionListener token, int nonce)
1620 throws RemoteException {
Kenny Rootf1121dc2010-09-29 07:30:53 -07001621 if (filename == null) {
1622 throw new IllegalArgumentException("filename cannot be null");
Kenny Rootf1121dc2010-09-29 07:30:53 -07001623 }
1624
Kenny Rootaf9d6672010-10-08 09:21:39 -07001625 final int callerUid = Binder.getCallingUid();
1626 final ObbState obbState = new ObbState(filename, callerUid, token, nonce);
1627 final ObbAction action = new UnmountObbAction(obbState, force);
Kenny Roota02b8b02010-08-05 16:14:17 -07001628 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
Kenny Root02c87302010-07-01 08:10:18 -07001629
Kenny Roota02b8b02010-08-05 16:14:17 -07001630 if (DEBUG_OBB)
1631 Slog.i(TAG, "Send to OBB handler: " + action.toString());
1632 }
1633
Jason parks5af0b912010-11-29 09:05:25 -06001634 public int decryptStorage(String password) {
1635 if (password == null) {
1636 throw new IllegalArgumentException("password cannot be null");
1637 }
1638
Jason parks8888c592011-01-20 22:46:41 -06001639 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
1640 "no permission to access the crypt keeper");
Jason parks5af0b912010-11-29 09:05:25 -06001641
1642 waitForReady();
1643
1644 if (DEBUG_EVENTS) {
1645 Slog.i(TAG, "decrypting storage...");
1646 }
1647
1648 try {
Jason parks9ed98bc2011-01-17 09:58:35 -06001649 ArrayList<String> rsp = mConnector.doCommand("cryptfs checkpw " + password);
1650 String []tok = rsp.get(0).split(" ");
1651
1652 if (tok == null || tok.length != 2) {
1653 return -1;
1654 }
1655
1656 int code = Integer.parseInt(tok[1]);
1657
1658 if (code == 0) {
1659 // Decrypt was successful. Post a delayed message before restarting in order
1660 // to let the UI to clear itself
1661 mHandler.postDelayed(new Runnable() {
1662 public void run() {
1663 mConnector.doCommand(String.format("cryptfs restart"));
1664 }
1665 }, 2000); // 2 seconds
1666 }
1667
1668 return code;
Jason parks5af0b912010-11-29 09:05:25 -06001669 } catch (NativeDaemonConnectorException e) {
1670 // Decryption failed
1671 return e.getCode();
1672 }
Jason parks5af0b912010-11-29 09:05:25 -06001673 }
1674
Jason parks56aa5322011-01-07 09:01:15 -06001675 public int encryptStorage(String password) {
1676 if (password == null) {
1677 throw new IllegalArgumentException("password cannot be null");
1678 }
1679
Jason parks8888c592011-01-20 22:46:41 -06001680 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
1681 "no permission to access the crypt keeper");
Jason parks56aa5322011-01-07 09:01:15 -06001682
1683 waitForReady();
1684
1685 if (DEBUG_EVENTS) {
Jason parks8888c592011-01-20 22:46:41 -06001686 Slog.i(TAG, "encrypting storage...");
Jason parks56aa5322011-01-07 09:01:15 -06001687 }
1688
1689 try {
Jason parks9ed98bc2011-01-17 09:58:35 -06001690 mConnector.doCommand(String.format("cryptfs enablecrypto inplace %s", password));
Jason parks56aa5322011-01-07 09:01:15 -06001691 } catch (NativeDaemonConnectorException e) {
1692 // Encryption failed
1693 return e.getCode();
1694 }
1695
1696 return 0;
1697 }
1698
Kenny Rootaf9d6672010-10-08 09:21:39 -07001699 private void addObbStateLocked(ObbState obbState) throws RemoteException {
1700 final IBinder binder = obbState.getBinder();
1701 List<ObbState> obbStates = mObbMounts.get(binder);
Kenny Root5919ac62010-10-05 09:49:40 -07001702
Kenny Rootaf9d6672010-10-08 09:21:39 -07001703 if (obbStates == null) {
1704 obbStates = new ArrayList<ObbState>();
1705 mObbMounts.put(binder, obbStates);
1706 } else {
1707 for (final ObbState o : obbStates) {
1708 if (o.filename.equals(obbState.filename)) {
1709 throw new IllegalStateException("Attempt to add ObbState twice. "
1710 + "This indicates an error in the MountService logic.");
Kenny Root5919ac62010-10-05 09:49:40 -07001711 }
1712 }
Kenny Roota02b8b02010-08-05 16:14:17 -07001713 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07001714
1715 obbStates.add(obbState);
1716 try {
1717 obbState.link();
1718 } catch (RemoteException e) {
1719 /*
1720 * The binder died before we could link it, so clean up our state
1721 * and return failure.
1722 */
1723 obbStates.remove(obbState);
1724 if (obbStates.isEmpty()) {
1725 mObbMounts.remove(binder);
1726 }
1727
1728 // Rethrow the error so mountObb can get it
1729 throw e;
1730 }
1731
1732 mObbPathToStateMap.put(obbState.filename, obbState);
Kenny Roota02b8b02010-08-05 16:14:17 -07001733 }
1734
Kenny Rootaf9d6672010-10-08 09:21:39 -07001735 private void removeObbStateLocked(ObbState obbState) {
1736 final IBinder binder = obbState.getBinder();
1737 final List<ObbState> obbStates = mObbMounts.get(binder);
1738 if (obbStates != null) {
1739 if (obbStates.remove(obbState)) {
1740 obbState.unlink();
Kenny Root05105f72010-09-22 17:29:43 -07001741 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07001742 if (obbStates.isEmpty()) {
1743 mObbMounts.remove(binder);
1744 }
Kenny Roota02b8b02010-08-05 16:14:17 -07001745 }
Kenny Roota02b8b02010-08-05 16:14:17 -07001746
Kenny Rootaf9d6672010-10-08 09:21:39 -07001747 mObbPathToStateMap.remove(obbState.filename);
Kenny Root38cf8862010-09-26 14:18:51 -07001748 }
1749
Kenny Roota02b8b02010-08-05 16:14:17 -07001750 private class ObbActionHandler extends Handler {
1751 private boolean mBound = false;
Kenny Root480afe72010-10-07 10:17:50 -07001752 private final List<ObbAction> mActions = new LinkedList<ObbAction>();
Kenny Roota02b8b02010-08-05 16:14:17 -07001753
1754 ObbActionHandler(Looper l) {
1755 super(l);
1756 }
1757
1758 @Override
1759 public void handleMessage(Message msg) {
1760 switch (msg.what) {
1761 case OBB_RUN_ACTION: {
Kenny Root480afe72010-10-07 10:17:50 -07001762 final ObbAction action = (ObbAction) msg.obj;
Kenny Roota02b8b02010-08-05 16:14:17 -07001763
1764 if (DEBUG_OBB)
1765 Slog.i(TAG, "OBB_RUN_ACTION: " + action.toString());
1766
1767 // If a bind was already initiated we don't really
1768 // need to do anything. The pending install
1769 // will be processed later on.
1770 if (!mBound) {
1771 // If this is the only one pending we might
1772 // have to bind to the service again.
1773 if (!connectToService()) {
1774 Slog.e(TAG, "Failed to bind to media container service");
1775 action.handleError();
1776 return;
Kenny Roota02b8b02010-08-05 16:14:17 -07001777 }
Kenny Roota02b8b02010-08-05 16:14:17 -07001778 }
Kenny Root735de3b2010-09-30 14:11:39 -07001779
Kenny Root735de3b2010-09-30 14:11:39 -07001780 mActions.add(action);
Kenny Roota02b8b02010-08-05 16:14:17 -07001781 break;
1782 }
1783 case OBB_MCS_BOUND: {
1784 if (DEBUG_OBB)
1785 Slog.i(TAG, "OBB_MCS_BOUND");
1786 if (msg.obj != null) {
1787 mContainerService = (IMediaContainerService) msg.obj;
1788 }
1789 if (mContainerService == null) {
1790 // Something seriously wrong. Bail out
1791 Slog.e(TAG, "Cannot bind to media container service");
1792 for (ObbAction action : mActions) {
1793 // Indicate service bind error
1794 action.handleError();
1795 }
1796 mActions.clear();
1797 } else if (mActions.size() > 0) {
Kenny Root480afe72010-10-07 10:17:50 -07001798 final ObbAction action = mActions.get(0);
Kenny Roota02b8b02010-08-05 16:14:17 -07001799 if (action != null) {
1800 action.execute(this);
1801 }
1802 } else {
1803 // Should never happen ideally.
1804 Slog.w(TAG, "Empty queue");
1805 }
1806 break;
1807 }
1808 case OBB_MCS_RECONNECT: {
1809 if (DEBUG_OBB)
1810 Slog.i(TAG, "OBB_MCS_RECONNECT");
1811 if (mActions.size() > 0) {
1812 if (mBound) {
1813 disconnectService();
1814 }
1815 if (!connectToService()) {
1816 Slog.e(TAG, "Failed to bind to media container service");
1817 for (ObbAction action : mActions) {
1818 // Indicate service bind error
1819 action.handleError();
1820 }
1821 mActions.clear();
1822 }
1823 }
1824 break;
1825 }
1826 case OBB_MCS_UNBIND: {
1827 if (DEBUG_OBB)
1828 Slog.i(TAG, "OBB_MCS_UNBIND");
1829
1830 // Delete pending install
1831 if (mActions.size() > 0) {
1832 mActions.remove(0);
1833 }
1834 if (mActions.size() == 0) {
1835 if (mBound) {
1836 disconnectService();
1837 }
1838 } else {
1839 // There are more pending requests in queue.
1840 // Just post MCS_BOUND message to trigger processing
1841 // of next pending install.
1842 mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND);
1843 }
1844 break;
1845 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07001846 case OBB_FLUSH_MOUNT_STATE: {
1847 final String path = (String) msg.obj;
1848
1849 if (DEBUG_OBB)
1850 Slog.i(TAG, "Flushing all OBB state for path " + path);
1851
1852 synchronized (mObbMounts) {
1853 final List<ObbState> obbStatesToRemove = new LinkedList<ObbState>();
1854
1855 final Iterator<Entry<String, ObbState>> i =
1856 mObbPathToStateMap.entrySet().iterator();
1857 while (i.hasNext()) {
1858 final Entry<String, ObbState> obbEntry = i.next();
1859
1860 /*
1861 * If this entry's source file is in the volume path
1862 * that got unmounted, remove it because it's no
1863 * longer valid.
1864 */
1865 if (obbEntry.getKey().startsWith(path)) {
1866 obbStatesToRemove.add(obbEntry.getValue());
1867 }
1868 }
1869
1870 for (final ObbState obbState : obbStatesToRemove) {
1871 if (DEBUG_OBB)
1872 Slog.i(TAG, "Removing state for " + obbState.filename);
1873
1874 removeObbStateLocked(obbState);
1875
1876 try {
1877 obbState.token.onObbResult(obbState.filename, obbState.nonce,
1878 OnObbStateChangeListener.UNMOUNTED);
1879 } catch (RemoteException e) {
1880 Slog.i(TAG, "Couldn't send unmount notification for OBB: "
1881 + obbState.filename);
1882 }
1883 }
1884 }
1885 break;
1886 }
Kenny Roota02b8b02010-08-05 16:14:17 -07001887 }
1888 }
1889
1890 private boolean connectToService() {
1891 if (DEBUG_OBB)
1892 Slog.i(TAG, "Trying to bind to DefaultContainerService");
1893
1894 Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
1895 if (mContext.bindService(service, mDefContainerConn, Context.BIND_AUTO_CREATE)) {
1896 mBound = true;
1897 return true;
1898 }
1899 return false;
1900 }
1901
1902 private void disconnectService() {
1903 mContainerService = null;
1904 mBound = false;
1905 mContext.unbindService(mDefContainerConn);
1906 }
1907 }
1908
1909 abstract class ObbAction {
1910 private static final int MAX_RETRIES = 3;
1911 private int mRetries;
1912
1913 ObbState mObbState;
1914
1915 ObbAction(ObbState obbState) {
1916 mObbState = obbState;
1917 }
1918
1919 public void execute(ObbActionHandler handler) {
1920 try {
1921 if (DEBUG_OBB)
1922 Slog.i(TAG, "Starting to execute action: " + this.toString());
1923 mRetries++;
1924 if (mRetries > MAX_RETRIES) {
1925 Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
Kenny Root480afe72010-10-07 10:17:50 -07001926 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
Kenny Roota02b8b02010-08-05 16:14:17 -07001927 handleError();
1928 return;
1929 } else {
1930 handleExecute();
1931 if (DEBUG_OBB)
1932 Slog.i(TAG, "Posting install MCS_UNBIND");
1933 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
1934 }
1935 } catch (RemoteException e) {
1936 if (DEBUG_OBB)
1937 Slog.i(TAG, "Posting install MCS_RECONNECT");
1938 mObbActionHandler.sendEmptyMessage(OBB_MCS_RECONNECT);
1939 } catch (Exception e) {
1940 if (DEBUG_OBB)
1941 Slog.d(TAG, "Error handling OBB action", e);
1942 handleError();
Kenny Root17eb6fb2010-10-06 15:02:52 -07001943 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
Kenny Roota02b8b02010-08-05 16:14:17 -07001944 }
1945 }
1946
Kenny Root05105f72010-09-22 17:29:43 -07001947 abstract void handleExecute() throws RemoteException, IOException;
Kenny Roota02b8b02010-08-05 16:14:17 -07001948 abstract void handleError();
Kenny Root38cf8862010-09-26 14:18:51 -07001949
1950 protected ObbInfo getObbInfo() throws IOException {
1951 ObbInfo obbInfo;
1952 try {
1953 obbInfo = mContainerService.getObbInfo(mObbState.filename);
1954 } catch (RemoteException e) {
1955 Slog.d(TAG, "Couldn't call DefaultContainerService to fetch OBB info for "
1956 + mObbState.filename);
1957 obbInfo = null;
1958 }
1959 if (obbInfo == null) {
1960 throw new IOException("Couldn't read OBB file: " + mObbState.filename);
1961 }
1962 return obbInfo;
1963 }
1964
Kenny Rootaf9d6672010-10-08 09:21:39 -07001965 protected void sendNewStatusOrIgnore(int status) {
1966 if (mObbState == null || mObbState.token == null) {
1967 return;
1968 }
1969
Kenny Root38cf8862010-09-26 14:18:51 -07001970 try {
Kenny Rootaf9d6672010-10-08 09:21:39 -07001971 mObbState.token.onObbResult(mObbState.filename, mObbState.nonce, status);
Kenny Root38cf8862010-09-26 14:18:51 -07001972 } catch (RemoteException e) {
1973 Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
1974 }
1975 }
Kenny Roota02b8b02010-08-05 16:14:17 -07001976 }
1977
1978 class MountObbAction extends ObbAction {
1979 private String mKey;
1980
1981 MountObbAction(ObbState obbState, String key) {
1982 super(obbState);
1983 mKey = key;
1984 }
1985
Jason parks5af0b912010-11-29 09:05:25 -06001986 @Override
Kenny Root735de3b2010-09-30 14:11:39 -07001987 public void handleExecute() throws IOException, RemoteException {
Kenny Rootaf9d6672010-10-08 09:21:39 -07001988 waitForReady();
1989 warnOnNotMounted();
1990
Kenny Root38cf8862010-09-26 14:18:51 -07001991 final ObbInfo obbInfo = getObbInfo();
1992
Kenny Roota02b8b02010-08-05 16:14:17 -07001993 if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mObbState.callerUid)) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07001994 Slog.w(TAG, "Denied attempt to mount OBB " + obbInfo.filename
1995 + " which is owned by " + obbInfo.packageName);
1996 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
1997 return;
Kenny Roota02b8b02010-08-05 16:14:17 -07001998 }
1999
Kenny Rootaf9d6672010-10-08 09:21:39 -07002000 final boolean isMounted;
2001 synchronized (mObbMounts) {
2002 isMounted = mObbPathToStateMap.containsKey(obbInfo.filename);
2003 }
2004 if (isMounted) {
2005 Slog.w(TAG, "Attempt to mount OBB which is already mounted: " + obbInfo.filename);
2006 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_ALREADY_MOUNTED);
2007 return;
2008 }
2009
2010 /*
2011 * The filename passed in might not be the canonical name, so just
2012 * set the filename to the canonicalized version.
2013 */
2014 mObbState.filename = obbInfo.filename;
2015
2016 final String hashedKey;
2017 if (mKey == null) {
2018 hashedKey = "none";
2019 } else {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002020 try {
Kenny Root3b1abba2010-10-13 15:00:07 -07002021 SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
2022
2023 KeySpec ks = new PBEKeySpec(mKey.toCharArray(), obbInfo.salt,
2024 PBKDF2_HASH_ROUNDS, CRYPTO_ALGORITHM_KEY_SIZE);
2025 SecretKey key = factory.generateSecret(ks);
2026 BigInteger bi = new BigInteger(key.getEncoded());
2027 hashedKey = bi.toString(16);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002028 } catch (NoSuchAlgorithmException e) {
Kenny Root3b1abba2010-10-13 15:00:07 -07002029 Slog.e(TAG, "Could not load PBKDF2 algorithm", e);
2030 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
2031 return;
2032 } catch (InvalidKeySpecException e) {
2033 Slog.e(TAG, "Invalid key spec when loading PBKDF2 algorithm", e);
2034 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
Kenny Root38cf8862010-09-26 14:18:51 -07002035 return;
2036 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07002037 }
Kenny Root38cf8862010-09-26 14:18:51 -07002038
Kenny Rootaf9d6672010-10-08 09:21:39 -07002039 int rc = StorageResultCode.OperationSucceeded;
2040 String cmd = String.format("obb mount %s %s %d", mObbState.filename, hashedKey,
2041 mObbState.callerUid);
2042 try {
2043 mConnector.doCommand(cmd);
2044 } catch (NativeDaemonConnectorException e) {
2045 int code = e.getCode();
2046 if (code != VoldResponseCode.OpFailedStorageBusy) {
2047 rc = StorageResultCode.OperationFailedInternalError;
Kenny Roota02b8b02010-08-05 16:14:17 -07002048 }
2049 }
2050
Kenny Rootaf9d6672010-10-08 09:21:39 -07002051 if (rc == StorageResultCode.OperationSucceeded) {
2052 if (DEBUG_OBB)
2053 Slog.d(TAG, "Successfully mounted OBB " + mObbState.filename);
2054
2055 synchronized (mObbMounts) {
2056 addObbStateLocked(mObbState);
2057 }
2058
2059 sendNewStatusOrIgnore(OnObbStateChangeListener.MOUNTED);
Kenny Root02c87302010-07-01 08:10:18 -07002060 } else {
Kenny Root05105f72010-09-22 17:29:43 -07002061 Slog.e(TAG, "Couldn't mount OBB file: " + rc);
Kenny Roota02b8b02010-08-05 16:14:17 -07002062
Kenny Rootaf9d6672010-10-08 09:21:39 -07002063 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT);
Kenny Root02c87302010-07-01 08:10:18 -07002064 }
2065 }
2066
Jason parks5af0b912010-11-29 09:05:25 -06002067 @Override
Kenny Roota02b8b02010-08-05 16:14:17 -07002068 public void handleError() {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002069 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
Kenny Root02c87302010-07-01 08:10:18 -07002070 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002071
2072 @Override
2073 public String toString() {
2074 StringBuilder sb = new StringBuilder();
2075 sb.append("MountObbAction{");
2076 sb.append("filename=");
2077 sb.append(mObbState.filename);
2078 sb.append(",callerUid=");
2079 sb.append(mObbState.callerUid);
2080 sb.append(",token=");
2081 sb.append(mObbState.token != null ? mObbState.token.toString() : "NULL");
Kenny Rootaf9d6672010-10-08 09:21:39 -07002082 sb.append(",binder=");
2083 sb.append(mObbState.token != null ? mObbState.getBinder().toString() : "null");
Kenny Roota02b8b02010-08-05 16:14:17 -07002084 sb.append('}');
2085 return sb.toString();
2086 }
2087 }
2088
2089 class UnmountObbAction extends ObbAction {
2090 private boolean mForceUnmount;
2091
2092 UnmountObbAction(ObbState obbState, boolean force) {
2093 super(obbState);
2094 mForceUnmount = force;
2095 }
2096
Jason parks5af0b912010-11-29 09:05:25 -06002097 @Override
Kenny Root38cf8862010-09-26 14:18:51 -07002098 public void handleExecute() throws IOException {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002099 waitForReady();
2100 warnOnNotMounted();
2101
Kenny Root38cf8862010-09-26 14:18:51 -07002102 final ObbInfo obbInfo = getObbInfo();
Kenny Roota02b8b02010-08-05 16:14:17 -07002103
Kenny Rootaf9d6672010-10-08 09:21:39 -07002104 final ObbState obbState;
Kenny Root38cf8862010-09-26 14:18:51 -07002105 synchronized (mObbMounts) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002106 obbState = mObbPathToStateMap.get(obbInfo.filename);
2107 }
Kenny Root38cf8862010-09-26 14:18:51 -07002108
Kenny Rootaf9d6672010-10-08 09:21:39 -07002109 if (obbState == null) {
2110 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_NOT_MOUNTED);
2111 return;
2112 }
2113
2114 if (obbState.callerUid != mObbState.callerUid) {
2115 Slog.w(TAG, "Permission denied attempting to unmount OBB " + obbInfo.filename
2116 + " (owned by " + obbInfo.packageName + ")");
2117 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
2118 return;
2119 }
2120
2121 mObbState.filename = obbInfo.filename;
2122
2123 int rc = StorageResultCode.OperationSucceeded;
2124 String cmd = String.format("obb unmount %s%s", mObbState.filename,
2125 (mForceUnmount ? " force" : ""));
2126 try {
2127 mConnector.doCommand(cmd);
2128 } catch (NativeDaemonConnectorException e) {
2129 int code = e.getCode();
2130 if (code == VoldResponseCode.OpFailedStorageBusy) {
2131 rc = StorageResultCode.OperationFailedStorageBusy;
2132 } else if (code == VoldResponseCode.OpFailedStorageNotFound) {
2133 // If it's not mounted then we've already won.
2134 rc = StorageResultCode.OperationSucceeded;
2135 } else {
2136 rc = StorageResultCode.OperationFailedInternalError;
Kenny Roota02b8b02010-08-05 16:14:17 -07002137 }
2138 }
2139
Kenny Rootaf9d6672010-10-08 09:21:39 -07002140 if (rc == StorageResultCode.OperationSucceeded) {
2141 synchronized (mObbMounts) {
2142 removeObbStateLocked(obbState);
Kenny Root38cf8862010-09-26 14:18:51 -07002143 }
2144
Kenny Rootaf9d6672010-10-08 09:21:39 -07002145 sendNewStatusOrIgnore(OnObbStateChangeListener.UNMOUNTED);
Kenny Roota02b8b02010-08-05 16:14:17 -07002146 } else {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002147 Slog.w(TAG, "Could not mount OBB: " + mObbState.filename);
2148 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_UNMOUNT);
Kenny Roota02b8b02010-08-05 16:14:17 -07002149 }
2150 }
2151
Jason parks5af0b912010-11-29 09:05:25 -06002152 @Override
Kenny Roota02b8b02010-08-05 16:14:17 -07002153 public void handleError() {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002154 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
Kenny Roota02b8b02010-08-05 16:14:17 -07002155 }
2156
2157 @Override
2158 public String toString() {
2159 StringBuilder sb = new StringBuilder();
2160 sb.append("UnmountObbAction{");
2161 sb.append("filename=");
2162 sb.append(mObbState.filename != null ? mObbState.filename : "null");
2163 sb.append(",force=");
2164 sb.append(mForceUnmount);
2165 sb.append(",callerUid=");
2166 sb.append(mObbState.callerUid);
2167 sb.append(",token=");
2168 sb.append(mObbState.token != null ? mObbState.token.toString() : "null");
Kenny Root735de3b2010-09-30 14:11:39 -07002169 sb.append(",binder=");
Kenny Rootaf9d6672010-10-08 09:21:39 -07002170 sb.append(mObbState.token != null ? mObbState.getBinder().toString() : "null");
Kenny Roota02b8b02010-08-05 16:14:17 -07002171 sb.append('}');
2172 return sb.toString();
2173 }
Kenny Root02c87302010-07-01 08:10:18 -07002174 }
Kenny Root38cf8862010-09-26 14:18:51 -07002175
2176 @Override
2177 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2178 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) {
2179 pw.println("Permission Denial: can't dump ActivityManager from from pid="
2180 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
2181 + " without permission " + android.Manifest.permission.DUMP);
2182 return;
2183 }
2184
Kenny Root38cf8862010-09-26 14:18:51 -07002185 synchronized (mObbMounts) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002186 pw.println(" mObbMounts:");
Kenny Root38cf8862010-09-26 14:18:51 -07002187
Kenny Rootaf9d6672010-10-08 09:21:39 -07002188 final Iterator<Entry<IBinder, List<ObbState>>> binders = mObbMounts.entrySet().iterator();
2189 while (binders.hasNext()) {
2190 Entry<IBinder, List<ObbState>> e = binders.next();
2191 pw.print(" Key="); pw.println(e.getKey().toString());
2192 final List<ObbState> obbStates = e.getValue();
Kenny Root38cf8862010-09-26 14:18:51 -07002193 for (final ObbState obbState : obbStates) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002194 pw.print(" "); pw.println(obbState.toString());
Kenny Root38cf8862010-09-26 14:18:51 -07002195 }
2196 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07002197
2198 pw.println("");
2199 pw.println(" mObbPathToStateMap:");
2200 final Iterator<Entry<String, ObbState>> maps = mObbPathToStateMap.entrySet().iterator();
2201 while (maps.hasNext()) {
2202 final Entry<String, ObbState> e = maps.next();
2203 pw.print(" "); pw.print(e.getKey());
2204 pw.print(" -> "); pw.println(e.getValue().toString());
2205 }
Kenny Root38cf8862010-09-26 14:18:51 -07002206 }
2207 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002208}
2209