blob: 7ec9b82f1e9100b04f972cba147ff0be08223ae2 [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
Jeff Sharkeyb049e212012-09-07 23:16:01 -070019import static android.content.pm.PackageManager.PERMISSION_GRANTED;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080020
Jason parks8888c592011-01-20 22:46:41 -060021import android.Manifest;
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -070022import android.app.AppOpsManager;
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;
Jeff Sharkeyb049e212012-09-07 23:16:01 -070030import android.content.pm.UserInfo;
Kenny Root02c87302010-07-01 08:10:18 -070031import android.content.res.ObbInfo;
Mike Lockwood2f6a3882011-05-09 19:08:06 -070032import android.content.res.Resources;
33import android.content.res.TypedArray;
34import android.content.res.XmlResourceParser;
Mike Lockwoodecedfdc2011-06-08 15:11:59 -070035import android.hardware.usb.UsbManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.net.Uri;
Kenny Root02c87302010-07-01 08:10:18 -070037import android.os.Binder;
Kenny Roota02b8b02010-08-05 16:14:17 -070038import android.os.Environment;
Jeff Sharkeyb049e212012-09-07 23:16:01 -070039import android.os.Environment.UserEnvironment;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080040import android.os.Handler;
Dianne Hackbornefa92b22013-05-03 14:11:43 -070041import android.os.HandlerThread;
Kenny Roota02b8b02010-08-05 16:14:17 -070042import android.os.IBinder;
Daniel Sandler5f27ef42010-03-16 15:42:02 -040043import android.os.Looper;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080044import android.os.Message;
San Mehat4270e1e2010-01-29 05:32:19 -080045import android.os.RemoteException;
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -080046import android.os.ServiceManager;
Svetoslavf23b64d2013-04-25 14:45:54 -070047import android.os.SystemClock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048import android.os.SystemProperties;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070049import android.os.UserHandle;
Kenny Roota02b8b02010-08-05 16:14:17 -070050import android.os.storage.IMountService;
51import android.os.storage.IMountServiceListener;
52import android.os.storage.IMountShutdownObserver;
53import android.os.storage.IObbActionListener;
Kenny Rootaf9d6672010-10-08 09:21:39 -070054import android.os.storage.OnObbStateChangeListener;
Paul Lawrence46791e72014-04-03 09:10:26 -070055import android.os.storage.StorageManager;
Kenny Roota02b8b02010-08-05 16:14:17 -070056import android.os.storage.StorageResultCode;
Mike Lockwood2f6a3882011-05-09 19:08:06 -070057import android.os.storage.StorageVolume;
Jason parksf7b3cd42011-01-27 09:28:25 -060058import android.text.TextUtils;
Mike Lockwood2f6a3882011-05-09 19:08:06 -070059import android.util.AttributeSet;
San Mehata5078592010-03-25 09:36:54 -070060import android.util.Slog;
Mike Lockwood2f6a3882011-05-09 19:08:06 -070061import android.util.Xml;
62
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -080063import com.android.internal.annotations.GuardedBy;
64import com.android.internal.annotations.VisibleForTesting;
Jeff Sharkeyb049e212012-09-07 23:16:01 -070065import com.android.internal.app.IMediaContainerService;
Jeff Sharkey5aca2b82013-10-16 16:21:54 -070066import com.android.internal.util.IndentingPrintWriter;
Jeff Sharkey4fbbda42012-09-24 18:34:07 -070067import com.android.internal.util.Preconditions;
Jeff Sharkeyb049e212012-09-07 23:16:01 -070068import com.android.internal.util.XmlUtils;
69import com.android.server.NativeDaemonConnector.Command;
Jeff Sharkey56cd6462013-06-07 15:09:15 -070070import com.android.server.NativeDaemonConnector.SensitiveArg;
Jeff Sharkeyb049e212012-09-07 23:16:01 -070071import com.android.server.am.ActivityManagerService;
72import com.android.server.pm.PackageManagerService;
73import com.android.server.pm.UserManagerService;
74import com.google.android.collect.Lists;
75import com.google.android.collect.Maps;
76
Paul Lawrence8e397362014-01-27 15:22:30 -080077import org.apache.commons.codec.binary.Hex;
Paul Lawrence945490c2014-03-27 16:37:28 +000078import org.apache.commons.codec.DecoderException;
Mike Lockwood2f6a3882011-05-09 19:08:06 -070079import org.xmlpull.v1.XmlPullParserException;
Kenny Roota02b8b02010-08-05 16:14:17 -070080
Jeff Sharkeyb049e212012-09-07 23:16:01 -070081import java.io.File;
Kenny Root38cf8862010-09-26 14:18:51 -070082import java.io.FileDescriptor;
Kenny Root05105f72010-09-22 17:29:43 -070083import java.io.IOException;
Kenny Root38cf8862010-09-26 14:18:51 -070084import java.io.PrintWriter;
Kenny Root3b1abba2010-10-13 15:00:07 -070085import java.math.BigInteger;
Paul Lawrence8e397362014-01-27 15:22:30 -080086import java.nio.charset.StandardCharsets;
Kenny Root735de3b2010-09-30 14:11:39 -070087import java.security.NoSuchAlgorithmException;
Kenny Root3b1abba2010-10-13 15:00:07 -070088import java.security.spec.InvalidKeySpecException;
89import java.security.spec.KeySpec;
San Mehat22dd86e2010-01-12 12:21:18 -080090import java.util.ArrayList;
Kenny Roota02b8b02010-08-05 16:14:17 -070091import java.util.HashMap;
San Mehat6cdd9c02010-02-09 14:45:20 -080092import java.util.HashSet;
Kenny Root38cf8862010-09-26 14:18:51 -070093import java.util.Iterator;
Kenny Roota02b8b02010-08-05 16:14:17 -070094import java.util.LinkedList;
95import java.util.List;
96import java.util.Map;
Kenny Root38cf8862010-09-26 14:18:51 -070097import java.util.Map.Entry;
Rickard Helldin5fb5df02014-02-07 09:35:04 +010098import java.util.concurrent.atomic.AtomicInteger;
Kenny Root51a573c2012-05-17 13:30:28 -070099import java.util.concurrent.CountDownLatch;
100import java.util.concurrent.TimeUnit;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101
Kenny Root3b1abba2010-10-13 15:00:07 -0700102import javax.crypto.SecretKey;
103import javax.crypto.SecretKeyFactory;
104import javax.crypto.spec.PBEKeySpec;
105
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106/**
San Mehatb1043402010-02-05 08:26:50 -0800107 * MountService implements back-end services for platform storage
108 * management.
109 * @hide - Applications should use android.os.storage.StorageManager
110 * to access the MountService.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111 */
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -0700112class MountService extends IMountService.Stub
113 implements INativeDaemonConnectorCallbacks, Watchdog.Monitor {
Jason parks5af0b912010-11-29 09:05:25 -0600114
Christopher Tated417d622013-08-19 16:14:25 -0700115 // Static direct instance pointer for the tightly-coupled idle service to use
116 static MountService sSelf = null;
117
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700118 // TODO: listen for user creation/deletion
119
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800120 private static final boolean LOCAL_LOGD = false;
121 private static final boolean DEBUG_UNMOUNT = false;
122 private static final boolean DEBUG_EVENTS = false;
Kenny Rootb7db2722011-01-25 16:39:35 -0800123 private static final boolean DEBUG_OBB = false;
Kenny Root02c87302010-07-01 08:10:18 -0700124
Kenny Root07714d42011-08-17 17:49:28 -0700125 // Disable this since it messes up long-running cryptfs operations.
126 private static final boolean WATCHDOG_ENABLE = false;
127
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800128 private static final String TAG = "MountService";
129
Kenny Root305bcbf2010-09-03 07:56:38 -0700130 private static final String VOLD_TAG = "VoldConnector";
131
Kenny Rootcf0b38c2011-03-22 14:17:59 -0700132 /** Maximum number of ASEC containers allowed to be mounted. */
133 private static final int MAX_CONTAINERS = 250;
134
San Mehat4270e1e2010-01-29 05:32:19 -0800135 /*
136 * Internal vold volume state constants
137 */
San Mehat7fd0fee2009-12-17 07:12:23 -0800138 class VolumeState {
139 public static final int Init = -1;
140 public static final int NoMedia = 0;
141 public static final int Idle = 1;
142 public static final int Pending = 2;
143 public static final int Checking = 3;
144 public static final int Mounted = 4;
145 public static final int Unmounting = 5;
146 public static final int Formatting = 6;
147 public static final int Shared = 7;
148 public static final int SharedMnt = 8;
149 }
150
San Mehat4270e1e2010-01-29 05:32:19 -0800151 /*
152 * Internal vold response code constants
153 */
San Mehat22dd86e2010-01-12 12:21:18 -0800154 class VoldResponseCode {
San Mehat4270e1e2010-01-29 05:32:19 -0800155 /*
156 * 100 series - Requestion action was initiated; expect another reply
157 * before proceeding with a new command.
158 */
San Mehat22dd86e2010-01-12 12:21:18 -0800159 public static final int VolumeListResult = 110;
160 public static final int AsecListResult = 111;
San Mehatc1b4ce92010-02-16 17:13:03 -0800161 public static final int StorageUsersListResult = 112;
Paul Lawrencee51dcf92014-03-18 10:56:00 -0700162 public static final int CryptfsGetfieldResult = 113;
San Mehat22dd86e2010-01-12 12:21:18 -0800163
San Mehat4270e1e2010-01-29 05:32:19 -0800164 /*
165 * 200 series - Requestion action has been successfully completed.
166 */
167 public static final int ShareStatusResult = 210;
San Mehat22dd86e2010-01-12 12:21:18 -0800168 public static final int AsecPathResult = 211;
San Mehat4270e1e2010-01-29 05:32:19 -0800169 public static final int ShareEnabledResult = 212;
San Mehat22dd86e2010-01-12 12:21:18 -0800170
San Mehat4270e1e2010-01-29 05:32:19 -0800171 /*
172 * 400 series - Command was accepted, but the requested action
173 * did not take place.
174 */
175 public static final int OpFailedNoMedia = 401;
176 public static final int OpFailedMediaBlank = 402;
177 public static final int OpFailedMediaCorrupt = 403;
178 public static final int OpFailedVolNotMounted = 404;
San Mehatd9709982010-02-18 11:43:03 -0800179 public static final int OpFailedStorageBusy = 405;
San Mehat2d66cef2010-03-23 11:12:52 -0700180 public static final int OpFailedStorageNotFound = 406;
San Mehat4270e1e2010-01-29 05:32:19 -0800181
182 /*
183 * 600 series - Unsolicited broadcasts.
184 */
San Mehat22dd86e2010-01-12 12:21:18 -0800185 public static final int VolumeStateChange = 605;
Jeff Sharkey5aca2b82013-10-16 16:21:54 -0700186 public static final int VolumeUuidChange = 613;
187 public static final int VolumeUserLabelChange = 614;
San Mehat22dd86e2010-01-12 12:21:18 -0800188 public static final int VolumeDiskInserted = 630;
189 public static final int VolumeDiskRemoved = 631;
190 public static final int VolumeBadRemoval = 632;
Svetoslavf23b64d2013-04-25 14:45:54 -0700191
192 /*
193 * 700 series - fstrim
194 */
195 public static final int FstrimCompleted = 700;
San Mehat22dd86e2010-01-12 12:21:18 -0800196 }
197
Paul Lawrence8e397362014-01-27 15:22:30 -0800198 /** List of crypto types.
199 * These must match CRYPT_TYPE_XXX in cryptfs.h AND their
200 * corresponding commands in CommandListener.cpp */
201 public static final String[] CRYPTO_TYPES
202 = { "password", "default", "pattern", "pin" };
203
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700204 private Context mContext;
205 private NativeDaemonConnector mConnector;
206
207 private final Object mVolumesLock = new Object();
208
209 /** When defined, base template for user-specific {@link StorageVolume}. */
210 private StorageVolume mEmulatedTemplate;
211
Jeff Sharkey1abdb712013-08-11 16:28:14 -0700212 // TODO: separate storage volumes on per-user basis
213
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -0800214 @GuardedBy("mVolumesLock")
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700215 private final ArrayList<StorageVolume> mVolumes = Lists.newArrayList();
216 /** Map from path to {@link StorageVolume} */
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -0800217 @GuardedBy("mVolumesLock")
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700218 private final HashMap<String, StorageVolume> mVolumesByPath = Maps.newHashMap();
219 /** Map from path to state */
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -0800220 @GuardedBy("mVolumesLock")
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700221 private final HashMap<String, String> mVolumeStates = Maps.newHashMap();
222
223 private volatile boolean mSystemReady = false;
224
San Mehat4270e1e2010-01-29 05:32:19 -0800225 private PackageManagerService mPms;
226 private boolean mUmsEnabling;
Mike Lockwoodecedfdc2011-06-08 15:11:59 -0700227 private boolean mUmsAvailable = false;
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800228 // Used as a lock for methods that register/unregister listeners.
229 final private ArrayList<MountServiceBinderListener> mListeners =
230 new ArrayList<MountServiceBinderListener>();
Jeff Sharkey0be607c2012-11-14 14:39:19 -0800231 private final CountDownLatch mConnectedSignal = new CountDownLatch(1);
232 private final CountDownLatch mAsecsScanned = new CountDownLatch(1);
San Mehat6a965af22010-02-24 17:47:30 -0800233 private boolean mSendUmsConnectedOnBoot = false;
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -0800234
San Mehat6cdd9c02010-02-09 14:45:20 -0800235 /**
236 * Private hash of currently mounted secure containers.
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800237 * Used as a lock in methods to manipulate secure containers.
San Mehat6cdd9c02010-02-09 14:45:20 -0800238 */
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800239 final private HashSet<String> mAsecMountSet = new HashSet<String>();
San Mehat6cdd9c02010-02-09 14:45:20 -0800240
Kenny Root02c87302010-07-01 08:10:18 -0700241 /**
Kenny Root3b1abba2010-10-13 15:00:07 -0700242 * The size of the crypto algorithm key in bits for OBB files. Currently
243 * Twofish is used which takes 128-bit keys.
244 */
245 private static final int CRYPTO_ALGORITHM_KEY_SIZE = 128;
246
247 /**
248 * The number of times to run SHA1 in the PBKDF2 function for OBB files.
249 * 1024 is reasonably secure and not too slow.
250 */
251 private static final int PBKDF2_HASH_ROUNDS = 1024;
252
253 /**
Kenny Roota02b8b02010-08-05 16:14:17 -0700254 * Mounted OBB tracking information. Used to track the current state of all
255 * OBBs.
Kenny Root02c87302010-07-01 08:10:18 -0700256 */
Kenny Root735de3b2010-09-30 14:11:39 -0700257 final private Map<IBinder, List<ObbState>> mObbMounts = new HashMap<IBinder, List<ObbState>>();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700258
259 /** Map from raw paths to {@link ObbState}. */
Kenny Roota02b8b02010-08-05 16:14:17 -0700260 final private Map<String, ObbState> mObbPathToStateMap = new HashMap<String, ObbState>();
261
262 class ObbState implements IBinder.DeathRecipient {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700263 public ObbState(String rawPath, String canonicalPath, int callingUid,
264 IObbActionListener token, int nonce) {
265 this.rawPath = rawPath;
266 this.canonicalPath = canonicalPath.toString();
267
268 final int userId = UserHandle.getUserId(callingUid);
269 this.ownerPath = buildObbPath(canonicalPath, userId, false);
270 this.voldPath = buildObbPath(canonicalPath, userId, true);
271
272 this.ownerGid = UserHandle.getSharedAppGid(callingUid);
Kenny Rootaf9d6672010-10-08 09:21:39 -0700273 this.token = token;
274 this.nonce = nonce;
Kenny Roota02b8b02010-08-05 16:14:17 -0700275 }
276
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700277 final String rawPath;
278 final String canonicalPath;
279 final String ownerPath;
280 final String voldPath;
Kenny Roota02b8b02010-08-05 16:14:17 -0700281
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700282 final int ownerGid;
Kenny Roota02b8b02010-08-05 16:14:17 -0700283
Kenny Rootaf9d6672010-10-08 09:21:39 -0700284 // Token of remote Binder caller
285 final IObbActionListener token;
286
287 // Identifier to pass back to the token
288 final int nonce;
Kenny Roota02b8b02010-08-05 16:14:17 -0700289
Kenny Root735de3b2010-09-30 14:11:39 -0700290 public IBinder getBinder() {
291 return token.asBinder();
292 }
293
Kenny Roota02b8b02010-08-05 16:14:17 -0700294 @Override
295 public void binderDied() {
296 ObbAction action = new UnmountObbAction(this, true);
297 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
Kenny Root735de3b2010-09-30 14:11:39 -0700298 }
Kenny Roota02b8b02010-08-05 16:14:17 -0700299
Kenny Root5919ac62010-10-05 09:49:40 -0700300 public void link() throws RemoteException {
301 getBinder().linkToDeath(this, 0);
302 }
303
304 public void unlink() {
Kenny Root735de3b2010-09-30 14:11:39 -0700305 getBinder().unlinkToDeath(this, 0);
Kenny Roota02b8b02010-08-05 16:14:17 -0700306 }
Kenny Root38cf8862010-09-26 14:18:51 -0700307
308 @Override
309 public String toString() {
310 StringBuilder sb = new StringBuilder("ObbState{");
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700311 sb.append("rawPath=").append(rawPath);
312 sb.append(",canonicalPath=").append(canonicalPath);
313 sb.append(",ownerPath=").append(ownerPath);
314 sb.append(",voldPath=").append(voldPath);
315 sb.append(",ownerGid=").append(ownerGid);
316 sb.append(",token=").append(token);
317 sb.append(",binder=").append(getBinder());
Kenny Root38cf8862010-09-26 14:18:51 -0700318 sb.append('}');
319 return sb.toString();
320 }
Kenny Roota02b8b02010-08-05 16:14:17 -0700321 }
322
323 // OBB Action Handler
324 final private ObbActionHandler mObbActionHandler;
325
326 // OBB action handler messages
327 private static final int OBB_RUN_ACTION = 1;
328 private static final int OBB_MCS_BOUND = 2;
329 private static final int OBB_MCS_UNBIND = 3;
330 private static final int OBB_MCS_RECONNECT = 4;
Kenny Rootaf9d6672010-10-08 09:21:39 -0700331 private static final int OBB_FLUSH_MOUNT_STATE = 5;
Kenny Roota02b8b02010-08-05 16:14:17 -0700332
333 /*
334 * Default Container Service information
335 */
336 static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
337 "com.android.defcontainer", "com.android.defcontainer.DefaultContainerService");
338
339 final private DefaultContainerConnection mDefContainerConn = new DefaultContainerConnection();
340
341 class DefaultContainerConnection implements ServiceConnection {
342 public void onServiceConnected(ComponentName name, IBinder service) {
343 if (DEBUG_OBB)
344 Slog.i(TAG, "onServiceConnected");
345 IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(service);
346 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_MCS_BOUND, imcs));
347 }
348
349 public void onServiceDisconnected(ComponentName name) {
350 if (DEBUG_OBB)
351 Slog.i(TAG, "onServiceDisconnected");
352 }
353 };
354
355 // Used in the ObbActionHandler
356 private IMediaContainerService mContainerService = null;
Kenny Root02c87302010-07-01 08:10:18 -0700357
358 // Handler messages
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800359 private static final int H_UNMOUNT_PM_UPDATE = 1;
360 private static final int H_UNMOUNT_PM_DONE = 2;
361 private static final int H_UNMOUNT_MS = 3;
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700362 private static final int H_SYSTEM_READY = 4;
Christopher Tated417d622013-08-19 16:14:25 -0700363 private static final int H_FSTRIM = 5;
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700364
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800365 private static final int RETRY_UNMOUNT_DELAY = 30; // in ms
366 private static final int MAX_UNMOUNT_RETRIES = 4;
367
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800368 class UnmountCallBack {
Kenny Root05105f72010-09-22 17:29:43 -0700369 final String path;
370 final boolean force;
Ben Komalo13c71972011-09-07 16:35:56 -0700371 final boolean removeEncryption;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800372 int retries;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800373
Ben Komalo13c71972011-09-07 16:35:56 -0700374 UnmountCallBack(String path, boolean force, boolean removeEncryption) {
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800375 retries = 0;
376 this.path = path;
377 this.force = force;
Ben Komalo13c71972011-09-07 16:35:56 -0700378 this.removeEncryption = removeEncryption;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800379 }
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800380
381 void handleFinished() {
San Mehata5078592010-03-25 09:36:54 -0700382 if (DEBUG_UNMOUNT) Slog.i(TAG, "Unmounting " + path);
Ben Komalo13c71972011-09-07 16:35:56 -0700383 doUnmountVolume(path, true, removeEncryption);
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800384 }
385 }
386
387 class UmsEnableCallBack extends UnmountCallBack {
Kenny Root05105f72010-09-22 17:29:43 -0700388 final String method;
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800389
390 UmsEnableCallBack(String path, String method, boolean force) {
Ben Komalo13c71972011-09-07 16:35:56 -0700391 super(path, force, false);
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800392 this.method = method;
393 }
394
395 @Override
396 void handleFinished() {
397 super.handleFinished();
398 doShareUnshareVolume(path, method, true);
399 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800400 }
401
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800402 class ShutdownCallBack extends UnmountCallBack {
Rickard Helldin5fb5df02014-02-07 09:35:04 +0100403 MountShutdownLatch mMountShutdownLatch;
404 ShutdownCallBack(String path, final MountShutdownLatch mountShutdownLatch) {
Ben Komalo13c71972011-09-07 16:35:56 -0700405 super(path, true, false);
Rickard Helldin5fb5df02014-02-07 09:35:04 +0100406 mMountShutdownLatch = mountShutdownLatch;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800407 }
408
409 @Override
410 void handleFinished() {
Ben Komalo13c71972011-09-07 16:35:56 -0700411 int ret = doUnmountVolume(path, true, removeEncryption);
Rickard Helldin5fb5df02014-02-07 09:35:04 +0100412 Slog.i(TAG, "Unmount completed: " + path + ", result code: " + ret);
413 mMountShutdownLatch.countDown();
414 }
415 }
416
417 static class MountShutdownLatch {
418 private IMountShutdownObserver mObserver;
419 private AtomicInteger mCount;
420
421 MountShutdownLatch(final IMountShutdownObserver observer, int count) {
422 mObserver = observer;
423 mCount = new AtomicInteger(count);
424 }
425
426 void countDown() {
427 boolean sendShutdown = false;
428 if (mCount.decrementAndGet() == 0) {
429 sendShutdown = true;
430 }
431 if (sendShutdown && mObserver != null) {
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800432 try {
Rickard Helldin5fb5df02014-02-07 09:35:04 +0100433 mObserver.onShutDownComplete(StorageResultCode.OperationSucceeded);
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800434 } catch (RemoteException e) {
San Mehata5078592010-03-25 09:36:54 -0700435 Slog.w(TAG, "RemoteException when shutting down");
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800436 }
437 }
438 }
439 }
440
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400441 class MountServiceHandler extends Handler {
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800442 ArrayList<UnmountCallBack> mForceUnmounts = new ArrayList<UnmountCallBack>();
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700443 boolean mUpdatingStatus = false;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800444
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400445 MountServiceHandler(Looper l) {
446 super(l);
447 }
448
Jason parks5af0b912010-11-29 09:05:25 -0600449 @Override
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800450 public void handleMessage(Message msg) {
451 switch (msg.what) {
452 case H_UNMOUNT_PM_UPDATE: {
San Mehata5078592010-03-25 09:36:54 -0700453 if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_UPDATE");
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800454 UnmountCallBack ucb = (UnmountCallBack) msg.obj;
455 mForceUnmounts.add(ucb);
San Mehata5078592010-03-25 09:36:54 -0700456 if (DEBUG_UNMOUNT) Slog.i(TAG, " registered = " + mUpdatingStatus);
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800457 // Register only if needed.
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700458 if (!mUpdatingStatus) {
San Mehata5078592010-03-25 09:36:54 -0700459 if (DEBUG_UNMOUNT) Slog.i(TAG, "Updating external media status on PackageManager");
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700460 mUpdatingStatus = true;
461 mPms.updateExternalMediaStatus(false, true);
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800462 }
463 break;
464 }
465 case H_UNMOUNT_PM_DONE: {
San Mehata5078592010-03-25 09:36:54 -0700466 if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_DONE");
San Mehata5078592010-03-25 09:36:54 -0700467 if (DEBUG_UNMOUNT) Slog.i(TAG, "Updated status. Processing requests");
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700468 mUpdatingStatus = false;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800469 int size = mForceUnmounts.size();
470 int sizeArr[] = new int[size];
471 int sizeArrN = 0;
Suchi Amalapurapu7af074a2010-04-05 16:46:32 -0700472 // Kill processes holding references first
473 ActivityManagerService ams = (ActivityManagerService)
474 ServiceManager.getService("activity");
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800475 for (int i = 0; i < size; i++) {
476 UnmountCallBack ucb = mForceUnmounts.get(i);
477 String path = ucb.path;
478 boolean done = false;
479 if (!ucb.force) {
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800480 done = true;
481 } else {
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800482 int pids[] = getStorageUsers(path);
483 if (pids == null || pids.length == 0) {
484 done = true;
485 } else {
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800486 // Eliminate system process here?
Dianne Hackborn64825172011-03-02 21:32:58 -0800487 ams.killPids(pids, "unmount media", true);
Suchi Amalapurapu7af074a2010-04-05 16:46:32 -0700488 // Confirm if file references have been freed.
489 pids = getStorageUsers(path);
490 if (pids == null || pids.length == 0) {
491 done = true;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800492 }
493 }
494 }
Suchi Amalapurapu7af074a2010-04-05 16:46:32 -0700495 if (!done && (ucb.retries < MAX_UNMOUNT_RETRIES)) {
496 // Retry again
497 Slog.i(TAG, "Retrying to kill storage users again");
498 mHandler.sendMessageDelayed(
499 mHandler.obtainMessage(H_UNMOUNT_PM_DONE,
500 ucb.retries++),
501 RETRY_UNMOUNT_DELAY);
502 } else {
503 if (ucb.retries >= MAX_UNMOUNT_RETRIES) {
504 Slog.i(TAG, "Failed to unmount media inspite of " +
505 MAX_UNMOUNT_RETRIES + " retries. Forcibly killing processes now");
506 }
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800507 sizeArr[sizeArrN++] = i;
508 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_MS,
509 ucb));
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800510 }
511 }
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800512 // Remove already processed elements from list.
513 for (int i = (sizeArrN-1); i >= 0; i--) {
514 mForceUnmounts.remove(sizeArr[i]);
515 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800516 break;
517 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700518 case H_UNMOUNT_MS: {
San Mehata5078592010-03-25 09:36:54 -0700519 if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_MS");
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800520 UnmountCallBack ucb = (UnmountCallBack) msg.obj;
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800521 ucb.handleFinished();
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800522 break;
523 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700524 case H_SYSTEM_READY: {
525 try {
526 handleSystemReady();
527 } catch (Exception ex) {
528 Slog.e(TAG, "Boot-time mount exception", ex);
529 }
530 break;
531 }
Christopher Tated417d622013-08-19 16:14:25 -0700532 case H_FSTRIM: {
533 waitForReady();
534 Slog.i(TAG, "Running fstrim idle maintenance");
535 try {
536 // This method must be run on the main (handler) thread,
537 // so it is safe to directly call into vold.
538 mConnector.execute("fstrim", "dotrim");
539 EventLogTags.writeFstrimStart(SystemClock.elapsedRealtime());
540 } catch (NativeDaemonConnectorException ndce) {
541 Slog.e(TAG, "Failed to run fstrim!");
542 }
543 // invoke the completion callback, if any
544 Runnable callback = (Runnable) msg.obj;
545 if (callback != null) {
546 callback.run();
547 }
548 break;
549 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800550 }
551 }
552 };
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700553
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700554 private final Handler mHandler;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800555
Kenny Root51a573c2012-05-17 13:30:28 -0700556 void waitForAsecScan() {
557 waitForLatch(mAsecsScanned);
558 }
559
San Mehat207e5382010-02-04 20:46:54 -0800560 private void waitForReady() {
Kenny Root51a573c2012-05-17 13:30:28 -0700561 waitForLatch(mConnectedSignal);
562 }
563
564 private void waitForLatch(CountDownLatch latch) {
Kenny Root51a573c2012-05-17 13:30:28 -0700565 for (;;) {
566 try {
567 if (latch.await(5000, TimeUnit.MILLISECONDS)) {
San Mehat207e5382010-02-04 20:46:54 -0800568 return;
Kenny Root51a573c2012-05-17 13:30:28 -0700569 } else {
570 Slog.w(TAG, "Thread " + Thread.currentThread().getName()
571 + " still waiting for MountService ready...");
San Mehat207e5382010-02-04 20:46:54 -0800572 }
Kenny Root51a573c2012-05-17 13:30:28 -0700573 } catch (InterruptedException e) {
574 Slog.w(TAG, "Interrupt while waiting for MountService to be ready.");
San Mehat207e5382010-02-04 20:46:54 -0800575 }
San Mehat207e5382010-02-04 20:46:54 -0800576 }
San Mehat1f6301e2010-01-07 22:40:27 -0800577 }
Kenny Root02c87302010-07-01 08:10:18 -0700578
Paul Lawrence945490c2014-03-27 16:37:28 +0000579 private boolean isReady() {
580 try {
581 return mConnectedSignal.await(0, TimeUnit.MILLISECONDS);
582 } catch (InterruptedException e) {
583 return false;
584 }
585 }
586
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700587 private void handleSystemReady() {
588 // Snapshot current volume states since it's not safe to call into vold
589 // while holding locks.
590 final HashMap<String, String> snapshot;
591 synchronized (mVolumesLock) {
592 snapshot = new HashMap<String, String>(mVolumeStates);
593 }
594
595 for (Map.Entry<String, String> entry : snapshot.entrySet()) {
596 final String path = entry.getKey();
597 final String state = entry.getValue();
598
599 if (state.equals(Environment.MEDIA_UNMOUNTED)) {
600 int rc = doMountVolume(path);
601 if (rc != StorageResultCode.OperationSucceeded) {
602 Slog.e(TAG, String.format("Boot-time mount failed (%d)",
603 rc));
604 }
605 } else if (state.equals(Environment.MEDIA_SHARED)) {
606 /*
607 * Bootstrap UMS enabled state since vold indicates
608 * the volume is shared (runtime restart while ums enabled)
609 */
610 notifyVolumeStateChange(null, path, VolumeState.NoMedia,
611 VolumeState.Shared);
612 }
613 }
614
615 // Push mounted state for all emulated storage
616 synchronized (mVolumesLock) {
617 for (StorageVolume volume : mVolumes) {
618 if (volume.isEmulated()) {
619 updatePublicVolumeState(volume, Environment.MEDIA_MOUNTED);
620 }
621 }
622 }
623
624 /*
625 * If UMS was connected on boot, send the connected event
626 * now that we're up.
627 */
628 if (mSendUmsConnectedOnBoot) {
629 sendUmsIntent(true);
630 mSendUmsConnectedOnBoot = false;
631 }
632 }
633
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700634 private final BroadcastReceiver mUserReceiver = new BroadcastReceiver() {
635 @Override
636 public void onReceive(Context context, Intent intent) {
637 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
638 if (userId == -1) return;
639 final UserHandle user = new UserHandle(userId);
640
641 final String action = intent.getAction();
642 if (Intent.ACTION_USER_ADDED.equals(action)) {
643 synchronized (mVolumesLock) {
644 createEmulatedVolumeForUserLocked(user);
645 }
646
647 } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
648 synchronized (mVolumesLock) {
649 final List<StorageVolume> toRemove = Lists.newArrayList();
650 for (StorageVolume volume : mVolumes) {
651 if (user.equals(volume.getOwner())) {
652 toRemove.add(volume);
653 }
654 }
655 for (StorageVolume volume : toRemove) {
656 removeVolumeLocked(volume);
657 }
658 }
659 }
660 }
661 };
662
663 private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
664 @Override
665 public void onReceive(Context context, Intent intent) {
666 boolean available = (intent.getBooleanExtra(UsbManager.USB_CONNECTED, false) &&
667 intent.getBooleanExtra(UsbManager.USB_FUNCTION_MASS_STORAGE, false));
668 notifyShareAvailabilityChange(available);
669 }
670 };
671
San Mehat4270e1e2010-01-29 05:32:19 -0800672 private final class MountServiceBinderListener implements IBinder.DeathRecipient {
673 final IMountServiceListener mListener;
674
675 MountServiceBinderListener(IMountServiceListener listener) {
676 mListener = listener;
Kenny Root02c87302010-07-01 08:10:18 -0700677
San Mehat91c77612010-01-07 10:39:41 -0800678 }
679
San Mehat4270e1e2010-01-29 05:32:19 -0800680 public void binderDied() {
San Mehata5078592010-03-25 09:36:54 -0700681 if (LOCAL_LOGD) Slog.d(TAG, "An IMountServiceListener has died!");
Kenny Roota02b8b02010-08-05 16:14:17 -0700682 synchronized (mListeners) {
San Mehat4270e1e2010-01-29 05:32:19 -0800683 mListeners.remove(this);
684 mListener.asBinder().unlinkToDeath(this, 0);
685 }
686 }
687 }
688
Christopher Tated417d622013-08-19 16:14:25 -0700689 void runIdleMaintenance(Runnable callback) {
690 mHandler.sendMessage(mHandler.obtainMessage(H_FSTRIM, callback));
691 }
692
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800693 private void doShareUnshareVolume(String path, String method, boolean enable) {
San Mehat4270e1e2010-01-29 05:32:19 -0800694 // TODO: Add support for multiple share methods
695 if (!method.equals("ums")) {
696 throw new IllegalArgumentException(String.format("Method %s not supported", method));
697 }
698
San Mehat4270e1e2010-01-29 05:32:19 -0800699 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -0800700 mConnector.execute("volume", enable ? "share" : "unshare", path, method);
San Mehat4270e1e2010-01-29 05:32:19 -0800701 } catch (NativeDaemonConnectorException e) {
San Mehata5078592010-03-25 09:36:54 -0700702 Slog.e(TAG, "Failed to share/unshare", e);
San Mehat4270e1e2010-01-29 05:32:19 -0800703 }
San Mehat4270e1e2010-01-29 05:32:19 -0800704 }
705
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700706 private void updatePublicVolumeState(StorageVolume volume, String state) {
707 final String path = volume.getPath();
708 final String oldState;
709 synchronized (mVolumesLock) {
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400710 oldState = mVolumeStates.put(path, state);
Jeff Sharkey1f706c62013-10-17 10:52:17 -0700711 volume.setState(state);
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400712 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700713
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400714 if (state.equals(oldState)) {
715 Slog.w(TAG, String.format("Duplicate state transition (%s -> %s) for %s",
716 state, state, path));
San Mehat4270e1e2010-01-29 05:32:19 -0800717 return;
718 }
San Mehatb1043402010-02-05 08:26:50 -0800719
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400720 Slog.d(TAG, "volume state changed for " + path + " (" + oldState + " -> " + state + ")");
Kenny Rootaf9d6672010-10-08 09:21:39 -0700721
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700722 // Tell PackageManager about changes to primary volume state, but only
723 // when not emulated.
724 if (volume.isPrimary() && !volume.isEmulated()) {
725 if (Environment.MEDIA_UNMOUNTED.equals(state)) {
726 mPms.updateExternalMediaStatus(false, false);
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400727
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700728 /*
729 * Some OBBs might have been unmounted when this volume was
730 * unmounted, so send a message to the handler to let it know to
731 * remove those from the list of mounted OBBS.
732 */
733 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(
734 OBB_FLUSH_MOUNT_STATE, path));
735 } else if (Environment.MEDIA_MOUNTED.equals(state)) {
736 mPms.updateExternalMediaStatus(true, false);
Mike Lockwood03559752010-07-19 18:25:03 -0400737 }
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800738 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700739
San Mehat4270e1e2010-01-29 05:32:19 -0800740 synchronized (mListeners) {
741 for (int i = mListeners.size() -1; i >= 0; i--) {
742 MountServiceBinderListener bl = mListeners.get(i);
743 try {
San Mehatb1043402010-02-05 08:26:50 -0800744 bl.mListener.onStorageStateChanged(path, oldState, state);
San Mehat4270e1e2010-01-29 05:32:19 -0800745 } catch (RemoteException rex) {
San Mehata5078592010-03-25 09:36:54 -0700746 Slog.e(TAG, "Listener dead");
San Mehat4270e1e2010-01-29 05:32:19 -0800747 mListeners.remove(i);
748 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700749 Slog.e(TAG, "Listener failed", ex);
San Mehat4270e1e2010-01-29 05:32:19 -0800750 }
751 }
752 }
753 }
754
755 /**
San Mehat4270e1e2010-01-29 05:32:19 -0800756 * Callback from NativeDaemonConnector
757 */
758 public void onDaemonConnected() {
759 /*
760 * Since we'll be calling back into the NativeDaemonConnector,
761 * we need to do our work in a new thread.
762 */
Kenny Root51a573c2012-05-17 13:30:28 -0700763 new Thread("MountService#onDaemonConnected") {
Jason parks5af0b912010-11-29 09:05:25 -0600764 @Override
San Mehat4270e1e2010-01-29 05:32:19 -0800765 public void run() {
766 /**
767 * Determine media state and UMS detection status
768 */
San Mehat4270e1e2010-01-29 05:32:19 -0800769 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -0800770 final String[] vols = NativeDaemonEvent.filterMessageList(
771 mConnector.executeForList("volume", "list"),
772 VoldResponseCode.VolumeListResult);
San Mehat4270e1e2010-01-29 05:32:19 -0800773 for (String volstr : vols) {
774 String[] tok = volstr.split(" ");
775 // FMT: <label> <mountpoint> <state>
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400776 String path = tok[1];
777 String state = Environment.MEDIA_REMOVED;
778
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700779 final StorageVolume volume;
780 synchronized (mVolumesLock) {
781 volume = mVolumesByPath.get(path);
782 }
783
San Mehat4270e1e2010-01-29 05:32:19 -0800784 int st = Integer.parseInt(tok[2]);
785 if (st == VolumeState.NoMedia) {
786 state = Environment.MEDIA_REMOVED;
787 } else if (st == VolumeState.Idle) {
San Mehat207e5382010-02-04 20:46:54 -0800788 state = Environment.MEDIA_UNMOUNTED;
San Mehat4270e1e2010-01-29 05:32:19 -0800789 } else if (st == VolumeState.Mounted) {
790 state = Environment.MEDIA_MOUNTED;
San Mehata5078592010-03-25 09:36:54 -0700791 Slog.i(TAG, "Media already mounted on daemon connection");
San Mehat4270e1e2010-01-29 05:32:19 -0800792 } else if (st == VolumeState.Shared) {
793 state = Environment.MEDIA_SHARED;
San Mehata5078592010-03-25 09:36:54 -0700794 Slog.i(TAG, "Media shared on daemon connection");
San Mehat4270e1e2010-01-29 05:32:19 -0800795 } else {
796 throw new Exception(String.format("Unexpected state %d", st));
797 }
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400798
799 if (state != null) {
800 if (DEBUG_EVENTS) Slog.i(TAG, "Updating valid state " + state);
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700801 updatePublicVolumeState(volume, state);
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400802 }
San Mehat4270e1e2010-01-29 05:32:19 -0800803 }
804 } catch (Exception e) {
San Mehata5078592010-03-25 09:36:54 -0700805 Slog.e(TAG, "Error processing initial volume state", e);
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700806 final StorageVolume primary = getPrimaryPhysicalVolume();
807 if (primary != null) {
808 updatePublicVolumeState(primary, Environment.MEDIA_REMOVED);
809 }
San Mehat4270e1e2010-01-29 05:32:19 -0800810 }
811
San Mehat207e5382010-02-04 20:46:54 -0800812 /*
Jason parks9ed98bc2011-01-17 09:58:35 -0600813 * Now that we've done our initialization, release
San Mehat207e5382010-02-04 20:46:54 -0800814 * the hounds!
815 */
Kenny Root51a573c2012-05-17 13:30:28 -0700816 mConnectedSignal.countDown();
Kenny Root51a573c2012-05-17 13:30:28 -0700817
818 // Let package manager load internal ASECs.
819 mPms.scanAvailableAsecs();
820
821 // Notify people waiting for ASECs to be scanned that it's done.
822 mAsecsScanned.countDown();
San Mehat4270e1e2010-01-29 05:32:19 -0800823 }
824 }.start();
825 }
826
827 /**
San Mehat4270e1e2010-01-29 05:32:19 -0800828 * Callback from NativeDaemonConnector
829 */
Dianne Hackborn77b987f2014-02-26 16:20:52 -0800830 public boolean onCheckHoldWakeLock(int code) {
831 return false;
832 }
833
834 /**
835 * Callback from NativeDaemonConnector
836 */
San Mehat4270e1e2010-01-29 05:32:19 -0800837 public boolean onEvent(int code, String raw, String[] cooked) {
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800838 if (DEBUG_EVENTS) {
839 StringBuilder builder = new StringBuilder();
840 builder.append("onEvent::");
841 builder.append(" raw= " + raw);
842 if (cooked != null) {
843 builder.append(" cooked = " );
844 for (String str : cooked) {
845 builder.append(" " + str);
846 }
847 }
San Mehata5078592010-03-25 09:36:54 -0700848 Slog.i(TAG, builder.toString());
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800849 }
San Mehat4270e1e2010-01-29 05:32:19 -0800850 if (code == VoldResponseCode.VolumeStateChange) {
851 /*
852 * One of the volumes we're managing has changed state.
853 * Format: "NNN Volume <label> <path> state changed
854 * from <old_#> (<old_str>) to <new_#> (<new_str>)"
855 */
856 notifyVolumeStateChange(
857 cooked[2], cooked[3], Integer.parseInt(cooked[7]),
858 Integer.parseInt(cooked[10]));
Jeff Sharkey5aca2b82013-10-16 16:21:54 -0700859 } else if (code == VoldResponseCode.VolumeUuidChange) {
860 // Format: nnn <label> <path> <uuid>
861 final String path = cooked[2];
862 final String uuid = (cooked.length > 3) ? cooked[3] : null;
863
864 final StorageVolume vol = mVolumesByPath.get(path);
865 if (vol != null) {
866 vol.setUuid(uuid);
867 }
868
869 } else if (code == VoldResponseCode.VolumeUserLabelChange) {
870 // Format: nnn <label> <path> <label>
871 final String path = cooked[2];
872 final String userLabel = (cooked.length > 3) ? cooked[3] : null;
873
874 final StorageVolume vol = mVolumesByPath.get(path);
875 if (vol != null) {
876 vol.setUserLabel(userLabel);
877 }
878
San Mehat4270e1e2010-01-29 05:32:19 -0800879 } else if ((code == VoldResponseCode.VolumeDiskInserted) ||
880 (code == VoldResponseCode.VolumeDiskRemoved) ||
881 (code == VoldResponseCode.VolumeBadRemoval)) {
882 // FMT: NNN Volume <label> <mountpoint> disk inserted (<major>:<minor>)
883 // FMT: NNN Volume <label> <mountpoint> disk removed (<major>:<minor>)
884 // FMT: NNN Volume <label> <mountpoint> bad removal (<major>:<minor>)
Mike Lockwooda5250c92011-05-23 13:44:04 -0400885 String action = null;
San Mehat4270e1e2010-01-29 05:32:19 -0800886 final String label = cooked[2];
887 final String path = cooked[3];
888 int major = -1;
889 int minor = -1;
890
891 try {
892 String devComp = cooked[6].substring(1, cooked[6].length() -1);
893 String[] devTok = devComp.split(":");
894 major = Integer.parseInt(devTok[0]);
895 minor = Integer.parseInt(devTok[1]);
896 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700897 Slog.e(TAG, "Failed to parse major/minor", ex);
San Mehat4270e1e2010-01-29 05:32:19 -0800898 }
899
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700900 final StorageVolume volume;
901 final String state;
902 synchronized (mVolumesLock) {
903 volume = mVolumesByPath.get(path);
904 state = mVolumeStates.get(path);
905 }
906
San Mehat4270e1e2010-01-29 05:32:19 -0800907 if (code == VoldResponseCode.VolumeDiskInserted) {
Dianne Hackborn8d044e82013-04-30 17:24:15 -0700908 new Thread("MountService#VolumeDiskInserted") {
Jason parks5af0b912010-11-29 09:05:25 -0600909 @Override
San Mehat4270e1e2010-01-29 05:32:19 -0800910 public void run() {
911 try {
912 int rc;
San Mehatb1043402010-02-05 08:26:50 -0800913 if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {
San Mehata5078592010-03-25 09:36:54 -0700914 Slog.w(TAG, String.format("Insertion mount failed (%d)", rc));
San Mehat4270e1e2010-01-29 05:32:19 -0800915 }
916 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700917 Slog.w(TAG, "Failed to mount media on insertion", ex);
San Mehat4270e1e2010-01-29 05:32:19 -0800918 }
919 }
920 }.start();
921 } else if (code == VoldResponseCode.VolumeDiskRemoved) {
922 /*
923 * This event gets trumped if we're already in BAD_REMOVAL state
924 */
925 if (getVolumeState(path).equals(Environment.MEDIA_BAD_REMOVAL)) {
926 return true;
927 }
928 /* Send the media unmounted event first */
San Mehata5078592010-03-25 09:36:54 -0700929 if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700930 updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
Johan Redestig0464c072014-01-18 22:46:56 +0100931 sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, volume, UserHandle.ALL);
San Mehat4270e1e2010-01-29 05:32:19 -0800932
San Mehata5078592010-03-25 09:36:54 -0700933 if (DEBUG_EVENTS) Slog.i(TAG, "Sending media removed");
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700934 updatePublicVolumeState(volume, Environment.MEDIA_REMOVED);
Mike Lockwooda5250c92011-05-23 13:44:04 -0400935 action = Intent.ACTION_MEDIA_REMOVED;
San Mehat4270e1e2010-01-29 05:32:19 -0800936 } else if (code == VoldResponseCode.VolumeBadRemoval) {
San Mehata5078592010-03-25 09:36:54 -0700937 if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
San Mehat4270e1e2010-01-29 05:32:19 -0800938 /* Send the media unmounted event first */
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700939 updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
Christian Beckf503c8f2013-05-20 08:42:45 +0200940 sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, volume, UserHandle.ALL);
San Mehat4270e1e2010-01-29 05:32:19 -0800941
San Mehata5078592010-03-25 09:36:54 -0700942 if (DEBUG_EVENTS) Slog.i(TAG, "Sending media bad removal");
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700943 updatePublicVolumeState(volume, Environment.MEDIA_BAD_REMOVAL);
Mike Lockwooda5250c92011-05-23 13:44:04 -0400944 action = Intent.ACTION_MEDIA_BAD_REMOVAL;
Svetoslavf23b64d2013-04-25 14:45:54 -0700945 } else if (code == VoldResponseCode.FstrimCompleted) {
Svetoslav9e814a82013-04-30 10:43:56 -0700946 EventLogTags.writeFstrimFinish(SystemClock.elapsedRealtime());
San Mehat4270e1e2010-01-29 05:32:19 -0800947 } else {
San Mehata5078592010-03-25 09:36:54 -0700948 Slog.e(TAG, String.format("Unknown code {%d}", code));
San Mehat4270e1e2010-01-29 05:32:19 -0800949 }
Mike Lockwooda5250c92011-05-23 13:44:04 -0400950
951 if (action != null) {
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700952 sendStorageIntent(action, volume, UserHandle.ALL);
Mike Lockwooda5250c92011-05-23 13:44:04 -0400953 }
San Mehat4270e1e2010-01-29 05:32:19 -0800954 } else {
955 return false;
956 }
957
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400958 return true;
San Mehat4270e1e2010-01-29 05:32:19 -0800959 }
960
San Mehat207e5382010-02-04 20:46:54 -0800961 private void notifyVolumeStateChange(String label, String path, int oldState, int newState) {
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700962 final StorageVolume volume;
963 final String state;
964 synchronized (mVolumesLock) {
965 volume = mVolumesByPath.get(path);
966 state = getVolumeState(path);
967 }
968
969 if (DEBUG_EVENTS) Slog.i(TAG, "notifyVolumeStateChange::" + state);
San Mehat4270e1e2010-01-29 05:32:19 -0800970
Mike Lockwooda5250c92011-05-23 13:44:04 -0400971 String action = null;
San Mehat4270e1e2010-01-29 05:32:19 -0800972
Mike Lockwoodbf2dd442010-03-03 06:16:52 -0500973 if (oldState == VolumeState.Shared && newState != oldState) {
San Mehata5078592010-03-25 09:36:54 -0700974 if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_UNSHARED intent");
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700975 sendStorageIntent(Intent.ACTION_MEDIA_UNSHARED, volume, UserHandle.ALL);
Mike Lockwoodbf2dd442010-03-03 06:16:52 -0500976 }
977
San Mehat4270e1e2010-01-29 05:32:19 -0800978 if (newState == VolumeState.Init) {
979 } else if (newState == VolumeState.NoMedia) {
980 // NoMedia is handled via Disk Remove events
981 } else if (newState == VolumeState.Idle) {
982 /*
983 * Don't notify if we're in BAD_REMOVAL, NOFS, UNMOUNTABLE, or
984 * if we're in the process of enabling UMS
985 */
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700986 if (!state.equals(
987 Environment.MEDIA_BAD_REMOVAL) && !state.equals(
988 Environment.MEDIA_NOFS) && !state.equals(
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800989 Environment.MEDIA_UNMOUNTABLE) && !getUmsEnabling()) {
San Mehata5078592010-03-25 09:36:54 -0700990 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state for media bad removal nofs and unmountable");
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700991 updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
Mike Lockwooda5250c92011-05-23 13:44:04 -0400992 action = Intent.ACTION_MEDIA_UNMOUNTED;
San Mehat4270e1e2010-01-29 05:32:19 -0800993 }
994 } else if (newState == VolumeState.Pending) {
995 } else if (newState == VolumeState.Checking) {
San Mehata5078592010-03-25 09:36:54 -0700996 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state checking");
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700997 updatePublicVolumeState(volume, Environment.MEDIA_CHECKING);
Mike Lockwooda5250c92011-05-23 13:44:04 -0400998 action = Intent.ACTION_MEDIA_CHECKING;
San Mehat4270e1e2010-01-29 05:32:19 -0800999 } else if (newState == VolumeState.Mounted) {
San Mehata5078592010-03-25 09:36:54 -07001000 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state mounted");
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001001 updatePublicVolumeState(volume, Environment.MEDIA_MOUNTED);
Mike Lockwooda5250c92011-05-23 13:44:04 -04001002 action = Intent.ACTION_MEDIA_MOUNTED;
San Mehat4270e1e2010-01-29 05:32:19 -08001003 } else if (newState == VolumeState.Unmounting) {
Mike Lockwooda5250c92011-05-23 13:44:04 -04001004 action = Intent.ACTION_MEDIA_EJECT;
San Mehat4270e1e2010-01-29 05:32:19 -08001005 } else if (newState == VolumeState.Formatting) {
1006 } else if (newState == VolumeState.Shared) {
San Mehata5078592010-03-25 09:36:54 -07001007 if (DEBUG_EVENTS) Slog.i(TAG, "Updating volume state media mounted");
San Mehat4270e1e2010-01-29 05:32:19 -08001008 /* Send the media unmounted event first */
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001009 updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
1010 sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, volume, UserHandle.ALL);
San Mehat4270e1e2010-01-29 05:32:19 -08001011
San Mehata5078592010-03-25 09:36:54 -07001012 if (DEBUG_EVENTS) Slog.i(TAG, "Updating media shared");
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001013 updatePublicVolumeState(volume, Environment.MEDIA_SHARED);
Mike Lockwooda5250c92011-05-23 13:44:04 -04001014 action = Intent.ACTION_MEDIA_SHARED;
San Mehata5078592010-03-25 09:36:54 -07001015 if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_SHARED intent");
San Mehat4270e1e2010-01-29 05:32:19 -08001016 } else if (newState == VolumeState.SharedMnt) {
San Mehata5078592010-03-25 09:36:54 -07001017 Slog.e(TAG, "Live shared mounts not supported yet!");
San Mehat4270e1e2010-01-29 05:32:19 -08001018 return;
1019 } else {
San Mehata5078592010-03-25 09:36:54 -07001020 Slog.e(TAG, "Unhandled VolumeState {" + newState + "}");
San Mehat4270e1e2010-01-29 05:32:19 -08001021 }
1022
Mike Lockwooda5250c92011-05-23 13:44:04 -04001023 if (action != null) {
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001024 sendStorageIntent(action, volume, UserHandle.ALL);
San Mehat4270e1e2010-01-29 05:32:19 -08001025 }
1026 }
1027
San Mehat207e5382010-02-04 20:46:54 -08001028 private int doMountVolume(String path) {
San Mehatb1043402010-02-05 08:26:50 -08001029 int rc = StorageResultCode.OperationSucceeded;
San Mehat207e5382010-02-04 20:46:54 -08001030
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001031 final StorageVolume volume;
1032 synchronized (mVolumesLock) {
1033 volume = mVolumesByPath.get(path);
1034 }
1035
San Mehata5078592010-03-25 09:36:54 -07001036 if (DEBUG_EVENTS) Slog.i(TAG, "doMountVolume: Mouting " + path);
San Mehat207e5382010-02-04 20:46:54 -08001037 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001038 mConnector.execute("volume", "mount", path);
San Mehat207e5382010-02-04 20:46:54 -08001039 } catch (NativeDaemonConnectorException e) {
1040 /*
1041 * Mount failed for some reason
1042 */
Mike Lockwooda5250c92011-05-23 13:44:04 -04001043 String action = null;
San Mehat207e5382010-02-04 20:46:54 -08001044 int code = e.getCode();
1045 if (code == VoldResponseCode.OpFailedNoMedia) {
1046 /*
1047 * Attempt to mount but no media inserted
1048 */
San Mehatb1043402010-02-05 08:26:50 -08001049 rc = StorageResultCode.OperationFailedNoMedia;
San Mehat207e5382010-02-04 20:46:54 -08001050 } else if (code == VoldResponseCode.OpFailedMediaBlank) {
San Mehata5078592010-03-25 09:36:54 -07001051 if (DEBUG_EVENTS) Slog.i(TAG, " updating volume state :: media nofs");
San Mehat207e5382010-02-04 20:46:54 -08001052 /*
1053 * Media is blank or does not contain a supported filesystem
1054 */
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001055 updatePublicVolumeState(volume, Environment.MEDIA_NOFS);
Mike Lockwooda5250c92011-05-23 13:44:04 -04001056 action = Intent.ACTION_MEDIA_NOFS;
San Mehatb1043402010-02-05 08:26:50 -08001057 rc = StorageResultCode.OperationFailedMediaBlank;
San Mehat207e5382010-02-04 20:46:54 -08001058 } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
San Mehata5078592010-03-25 09:36:54 -07001059 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state media corrupt");
San Mehat207e5382010-02-04 20:46:54 -08001060 /*
1061 * Volume consistency check failed
1062 */
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001063 updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTABLE);
Mike Lockwooda5250c92011-05-23 13:44:04 -04001064 action = Intent.ACTION_MEDIA_UNMOUNTABLE;
San Mehatb1043402010-02-05 08:26:50 -08001065 rc = StorageResultCode.OperationFailedMediaCorrupt;
San Mehat207e5382010-02-04 20:46:54 -08001066 } else {
San Mehatb1043402010-02-05 08:26:50 -08001067 rc = StorageResultCode.OperationFailedInternalError;
San Mehat207e5382010-02-04 20:46:54 -08001068 }
1069
1070 /*
1071 * Send broadcast intent (if required for the failure)
1072 */
Mike Lockwooda5250c92011-05-23 13:44:04 -04001073 if (action != null) {
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001074 sendStorageIntent(action, volume, UserHandle.ALL);
San Mehat207e5382010-02-04 20:46:54 -08001075 }
1076 }
1077
1078 return rc;
1079 }
1080
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -08001081 /*
1082 * If force is not set, we do not unmount if there are
1083 * processes holding references to the volume about to be unmounted.
1084 * If force is set, all the processes holding references need to be
1085 * killed via the ActivityManager before actually unmounting the volume.
1086 * This might even take a while and might be retried after timed delays
1087 * to make sure we dont end up in an instable state and kill some core
1088 * processes.
Ben Komalo13c71972011-09-07 16:35:56 -07001089 * If removeEncryption is set, force is implied, and the system will remove any encryption
1090 * mapping set on the volume when unmounting.
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -08001091 */
Ben Komalo13c71972011-09-07 16:35:56 -07001092 private int doUnmountVolume(String path, boolean force, boolean removeEncryption) {
San Mehat59443a62010-02-09 13:28:45 -08001093 if (!getVolumeState(path).equals(Environment.MEDIA_MOUNTED)) {
San Mehat207e5382010-02-04 20:46:54 -08001094 return VoldResponseCode.OpFailedVolNotMounted;
1095 }
Kenny Rootaa485402010-09-14 14:49:41 -07001096
1097 /*
1098 * Force a GC to make sure AssetManagers in other threads of the
1099 * system_server are cleaned up. We have to do this since AssetManager
1100 * instances are kept as a WeakReference and it's possible we have files
1101 * open on the external storage.
1102 */
1103 Runtime.getRuntime().gc();
1104
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -08001105 // Redundant probably. But no harm in updating state again.
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -07001106 mPms.updateExternalMediaStatus(false, false);
San Mehat207e5382010-02-04 20:46:54 -08001107 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001108 final Command cmd = new Command("volume", "unmount", path);
1109 if (removeEncryption) {
1110 cmd.appendArg("force_and_revert");
1111 } else if (force) {
1112 cmd.appendArg("force");
1113 }
1114 mConnector.execute(cmd);
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -07001115 // We unmounted the volume. None of the asec containers are available now.
1116 synchronized (mAsecMountSet) {
1117 mAsecMountSet.clear();
1118 }
San Mehatb1043402010-02-05 08:26:50 -08001119 return StorageResultCode.OperationSucceeded;
San Mehat207e5382010-02-04 20:46:54 -08001120 } catch (NativeDaemonConnectorException e) {
1121 // Don't worry about mismatch in PackageManager since the
1122 // call back will handle the status changes any way.
1123 int code = e.getCode();
1124 if (code == VoldResponseCode.OpFailedVolNotMounted) {
San Mehata181b212010-02-11 06:50:20 -08001125 return StorageResultCode.OperationFailedStorageNotMounted;
San Mehatd9709982010-02-18 11:43:03 -08001126 } else if (code == VoldResponseCode.OpFailedStorageBusy) {
1127 return StorageResultCode.OperationFailedStorageBusy;
San Mehat207e5382010-02-04 20:46:54 -08001128 } else {
San Mehatb1043402010-02-05 08:26:50 -08001129 return StorageResultCode.OperationFailedInternalError;
San Mehat207e5382010-02-04 20:46:54 -08001130 }
1131 }
1132 }
1133
1134 private int doFormatVolume(String path) {
1135 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001136 mConnector.execute("volume", "format", path);
San Mehatb1043402010-02-05 08:26:50 -08001137 return StorageResultCode.OperationSucceeded;
San Mehat207e5382010-02-04 20:46:54 -08001138 } catch (NativeDaemonConnectorException e) {
1139 int code = e.getCode();
1140 if (code == VoldResponseCode.OpFailedNoMedia) {
San Mehatb1043402010-02-05 08:26:50 -08001141 return StorageResultCode.OperationFailedNoMedia;
San Mehat207e5382010-02-04 20:46:54 -08001142 } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
San Mehatb1043402010-02-05 08:26:50 -08001143 return StorageResultCode.OperationFailedMediaCorrupt;
San Mehat207e5382010-02-04 20:46:54 -08001144 } else {
San Mehatb1043402010-02-05 08:26:50 -08001145 return StorageResultCode.OperationFailedInternalError;
San Mehat207e5382010-02-04 20:46:54 -08001146 }
1147 }
1148 }
1149
San Mehatb1043402010-02-05 08:26:50 -08001150 private boolean doGetVolumeShared(String path, String method) {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001151 final NativeDaemonEvent event;
Kenny Roota80ce062010-06-01 13:23:53 -07001152 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001153 event = mConnector.execute("volume", "shared", path, method);
Kenny Roota80ce062010-06-01 13:23:53 -07001154 } catch (NativeDaemonConnectorException ex) {
1155 Slog.e(TAG, "Failed to read response to volume shared " + path + " " + method);
1156 return false;
1157 }
San Mehatb1043402010-02-05 08:26:50 -08001158
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001159 if (event.getCode() == VoldResponseCode.ShareEnabledResult) {
1160 return event.getMessage().endsWith("enabled");
1161 } else {
1162 return false;
San Mehatb1043402010-02-05 08:26:50 -08001163 }
San Mehatb1043402010-02-05 08:26:50 -08001164 }
1165
Mike Lockwoodecedfdc2011-06-08 15:11:59 -07001166 private void notifyShareAvailabilityChange(final boolean avail) {
San Mehat4270e1e2010-01-29 05:32:19 -08001167 synchronized (mListeners) {
Mike Lockwoodecedfdc2011-06-08 15:11:59 -07001168 mUmsAvailable = avail;
San Mehat4270e1e2010-01-29 05:32:19 -08001169 for (int i = mListeners.size() -1; i >= 0; i--) {
1170 MountServiceBinderListener bl = mListeners.get(i);
1171 try {
San Mehatb1043402010-02-05 08:26:50 -08001172 bl.mListener.onUsbMassStorageConnectionChanged(avail);
San Mehat4270e1e2010-01-29 05:32:19 -08001173 } catch (RemoteException rex) {
San Mehata5078592010-03-25 09:36:54 -07001174 Slog.e(TAG, "Listener dead");
San Mehat4270e1e2010-01-29 05:32:19 -08001175 mListeners.remove(i);
1176 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -07001177 Slog.e(TAG, "Listener failed", ex);
San Mehat4270e1e2010-01-29 05:32:19 -08001178 }
1179 }
1180 }
1181
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001182 if (mSystemReady == true) {
San Mehat6a965af22010-02-24 17:47:30 -08001183 sendUmsIntent(avail);
1184 } else {
1185 mSendUmsConnectedOnBoot = avail;
San Mehat4270e1e2010-01-29 05:32:19 -08001186 }
San Mehat2fe718a2010-03-11 12:01:49 -08001187
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001188 final StorageVolume primary = getPrimaryPhysicalVolume();
1189 if (avail == false && primary != null
1190 && Environment.MEDIA_SHARED.equals(getVolumeState(primary.getPath()))) {
1191 final String path = primary.getPath();
San Mehat2fe718a2010-03-11 12:01:49 -08001192 /*
1193 * USB mass storage disconnected while enabled
1194 */
Dianne Hackborn8d044e82013-04-30 17:24:15 -07001195 new Thread("MountService#AvailabilityChange") {
Jason parks5af0b912010-11-29 09:05:25 -06001196 @Override
San Mehat2fe718a2010-03-11 12:01:49 -08001197 public void run() {
1198 try {
1199 int rc;
San Mehata5078592010-03-25 09:36:54 -07001200 Slog.w(TAG, "Disabling UMS after cable disconnect");
San Mehat2fe718a2010-03-11 12:01:49 -08001201 doShareUnshareVolume(path, "ums", false);
1202 if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {
San Mehata5078592010-03-25 09:36:54 -07001203 Slog.e(TAG, String.format(
San Mehat2fe718a2010-03-11 12:01:49 -08001204 "Failed to remount {%s} on UMS enabled-disconnect (%d)",
1205 path, rc));
1206 }
1207 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -07001208 Slog.w(TAG, "Failed to mount media on UMS enabled-disconnect", ex);
San Mehat2fe718a2010-03-11 12:01:49 -08001209 }
1210 }
1211 }.start();
1212 }
San Mehat4270e1e2010-01-29 05:32:19 -08001213 }
1214
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001215 private void sendStorageIntent(String action, StorageVolume volume, UserHandle user) {
1216 final Intent intent = new Intent(action, Uri.parse("file://" + volume.getPath()));
1217 intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, volume);
Johan Redestig0464c072014-01-18 22:46:56 +01001218 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001219 Slog.d(TAG, "sendStorageIntent " + intent + " to " + user);
1220 mContext.sendBroadcastAsUser(intent, user);
Mike Lockwooda5250c92011-05-23 13:44:04 -04001221 }
1222
San Mehat6a965af22010-02-24 17:47:30 -08001223 private void sendUmsIntent(boolean c) {
Dianne Hackborn5ac72a22012-08-29 18:32:08 -07001224 mContext.sendBroadcastAsUser(
1225 new Intent((c ? Intent.ACTION_UMS_CONNECTED : Intent.ACTION_UMS_DISCONNECTED)),
1226 UserHandle.ALL);
San Mehat6a965af22010-02-24 17:47:30 -08001227 }
1228
San Mehat207e5382010-02-04 20:46:54 -08001229 private void validatePermission(String perm) {
San Mehat4270e1e2010-01-29 05:32:19 -08001230 if (mContext.checkCallingOrSelfPermission(perm) != PackageManager.PERMISSION_GRANTED) {
1231 throw new SecurityException(String.format("Requires %s permission", perm));
1232 }
1233 }
1234
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001235 // Storage list XML tags
1236 private static final String TAG_STORAGE_LIST = "StorageList";
1237 private static final String TAG_STORAGE = "storage";
1238
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001239 private void readStorageListLocked() {
1240 mVolumes.clear();
1241 mVolumeStates.clear();
1242
Fabrice Di Meglio13fe2a52012-05-18 18:08:58 -07001243 Resources resources = mContext.getResources();
1244
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001245 int id = com.android.internal.R.xml.storage_list;
1246 XmlResourceParser parser = resources.getXml(id);
1247 AttributeSet attrs = Xml.asAttributeSet(parser);
1248
1249 try {
1250 XmlUtils.beginDocument(parser, TAG_STORAGE_LIST);
1251 while (true) {
1252 XmlUtils.nextElement(parser);
1253
1254 String element = parser.getName();
1255 if (element == null) break;
1256
1257 if (TAG_STORAGE.equals(element)) {
1258 TypedArray a = resources.obtainAttributes(attrs,
1259 com.android.internal.R.styleable.Storage);
1260
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001261 String path = a.getString(
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001262 com.android.internal.R.styleable.Storage_mountPoint);
Fabrice Di Meglio13fe2a52012-05-18 18:08:58 -07001263 int descriptionId = a.getResourceId(
1264 com.android.internal.R.styleable.Storage_storageDescription, -1);
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001265 CharSequence description = a.getText(
1266 com.android.internal.R.styleable.Storage_storageDescription);
1267 boolean primary = a.getBoolean(
1268 com.android.internal.R.styleable.Storage_primary, false);
1269 boolean removable = a.getBoolean(
1270 com.android.internal.R.styleable.Storage_removable, false);
1271 boolean emulated = a.getBoolean(
1272 com.android.internal.R.styleable.Storage_emulated, false);
1273 int mtpReserve = a.getInt(
1274 com.android.internal.R.styleable.Storage_mtpReserve, 0);
Mike Lockwood8e8b2802011-06-07 08:03:33 -07001275 boolean allowMassStorage = a.getBoolean(
1276 com.android.internal.R.styleable.Storage_allowMassStorage, false);
Mike Lockwood7a59dd22011-07-11 09:18:03 -04001277 // resource parser does not support longs, so XML value is in megabytes
1278 long maxFileSize = a.getInt(
1279 com.android.internal.R.styleable.Storage_maxFileSize, 0) * 1024L * 1024L;
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001280
1281 Slog.d(TAG, "got storage path: " + path + " description: " + description +
1282 " primary: " + primary + " removable: " + removable +
Mike Lockwood8e8b2802011-06-07 08:03:33 -07001283 " emulated: " + emulated + " mtpReserve: " + mtpReserve +
Mike Lockwood7a59dd22011-07-11 09:18:03 -04001284 " allowMassStorage: " + allowMassStorage +
1285 " maxFileSize: " + maxFileSize);
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001286
1287 if (emulated) {
1288 // For devices with emulated storage, we create separate
1289 // volumes for each known user.
1290 mEmulatedTemplate = new StorageVolume(null, descriptionId, true, false,
1291 true, mtpReserve, false, maxFileSize, null);
1292
1293 final UserManagerService userManager = UserManagerService.getInstance();
Amith Yamasani920ace02012-09-20 22:15:37 -07001294 for (UserInfo user : userManager.getUsers(false)) {
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001295 createEmulatedVolumeForUserLocked(user.getUserHandle());
1296 }
1297
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001298 } else {
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001299 if (path == null || description == null) {
1300 Slog.e(TAG, "Missing storage path or description in readStorageList");
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001301 } else {
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001302 final StorageVolume volume = new StorageVolume(new File(path),
1303 descriptionId, primary, removable, emulated, mtpReserve,
1304 allowMassStorage, maxFileSize, null);
1305 addVolumeLocked(volume);
Jeff Sharkey44cbdec2013-10-07 16:49:47 -07001306
1307 // Until we hear otherwise, treat as unmounted
1308 mVolumeStates.put(volume.getPath(), Environment.MEDIA_UNMOUNTED);
Jeff Sharkey1f706c62013-10-17 10:52:17 -07001309 volume.setState(Environment.MEDIA_UNMOUNTED);
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001310 }
1311 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001312
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001313 a.recycle();
1314 }
1315 }
1316 } catch (XmlPullParserException e) {
1317 throw new RuntimeException(e);
1318 } catch (IOException e) {
1319 throw new RuntimeException(e);
1320 } finally {
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001321 // Compute storage ID for each physical volume; emulated storage is
1322 // always 0 when defined.
1323 int index = isExternalStorageEmulated() ? 1 : 0;
1324 for (StorageVolume volume : mVolumes) {
1325 if (!volume.isEmulated()) {
1326 volume.setStorageId(index++);
1327 }
Mike Lockwoodfbfe5552011-05-17 17:19:37 -04001328 }
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001329 parser.close();
1330 }
1331 }
1332
San Mehat4270e1e2010-01-29 05:32:19 -08001333 /**
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001334 * Create and add new {@link StorageVolume} for given {@link UserHandle}
1335 * using {@link #mEmulatedTemplate} as template.
1336 */
1337 private void createEmulatedVolumeForUserLocked(UserHandle user) {
1338 if (mEmulatedTemplate == null) {
1339 throw new IllegalStateException("Missing emulated volume multi-user template");
1340 }
1341
1342 final UserEnvironment userEnv = new UserEnvironment(user.getIdentifier());
1343 final File path = userEnv.getExternalStorageDirectory();
1344 final StorageVolume volume = StorageVolume.fromTemplate(mEmulatedTemplate, path, user);
1345 volume.setStorageId(0);
1346 addVolumeLocked(volume);
1347
1348 if (mSystemReady) {
1349 updatePublicVolumeState(volume, Environment.MEDIA_MOUNTED);
1350 } else {
1351 // Place stub status for early callers to find
1352 mVolumeStates.put(volume.getPath(), Environment.MEDIA_MOUNTED);
Jeff Sharkey1f706c62013-10-17 10:52:17 -07001353 volume.setState(Environment.MEDIA_MOUNTED);
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001354 }
1355 }
1356
1357 private void addVolumeLocked(StorageVolume volume) {
1358 Slog.d(TAG, "addVolumeLocked() " + volume);
1359 mVolumes.add(volume);
1360 final StorageVolume existing = mVolumesByPath.put(volume.getPath(), volume);
1361 if (existing != null) {
1362 throw new IllegalStateException(
1363 "Volume at " + volume.getPath() + " already exists: " + existing);
1364 }
1365 }
1366
1367 private void removeVolumeLocked(StorageVolume volume) {
1368 Slog.d(TAG, "removeVolumeLocked() " + volume);
1369 mVolumes.remove(volume);
1370 mVolumesByPath.remove(volume.getPath());
1371 mVolumeStates.remove(volume.getPath());
1372 }
1373
1374 private StorageVolume getPrimaryPhysicalVolume() {
1375 synchronized (mVolumesLock) {
1376 for (StorageVolume volume : mVolumes) {
1377 if (volume.isPrimary() && !volume.isEmulated()) {
1378 return volume;
1379 }
1380 }
1381 }
1382 return null;
1383 }
1384
1385 /**
San Mehat207e5382010-02-04 20:46:54 -08001386 * Constructs a new MountService instance
1387 *
1388 * @param context Binder context for this service
1389 */
1390 public MountService(Context context) {
Christopher Tated417d622013-08-19 16:14:25 -07001391 sSelf = this;
1392
San Mehat207e5382010-02-04 20:46:54 -08001393 mContext = context;
1394
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001395 synchronized (mVolumesLock) {
1396 readStorageListLocked();
Mike Lockwood03559752010-07-19 18:25:03 -04001397 }
1398
San Mehat207e5382010-02-04 20:46:54 -08001399 // XXX: This will go away soon in favor of IMountServiceObserver
1400 mPms = (PackageManagerService) ServiceManager.getService("package");
1401
Dianne Hackbornefa92b22013-05-03 14:11:43 -07001402 HandlerThread hthread = new HandlerThread(TAG);
1403 hthread.start();
1404 mHandler = new MountServiceHandler(hthread.getLooper());
Daniel Sandler5f27ef42010-03-16 15:42:02 -04001405
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001406 // Watch for user changes
1407 final IntentFilter userFilter = new IntentFilter();
1408 userFilter.addAction(Intent.ACTION_USER_ADDED);
1409 userFilter.addAction(Intent.ACTION_USER_REMOVED);
1410 mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);
1411
1412 // Watch for USB changes on primary volume
1413 final StorageVolume primary = getPrimaryPhysicalVolume();
1414 if (primary != null && primary.allowMassStorage()) {
1415 mContext.registerReceiver(
1416 mUsbReceiver, new IntentFilter(UsbManager.ACTION_USB_STATE), null, mHandler);
1417 }
1418
Kenny Roota02b8b02010-08-05 16:14:17 -07001419 // Add OBB Action Handler to MountService thread.
Dianne Hackborn8d044e82013-04-30 17:24:15 -07001420 mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper());
Kenny Roota02b8b02010-08-05 16:14:17 -07001421
Marco Nelissenc34ebce2010-02-18 13:39:41 -08001422 /*
Kenny Root305bcbf2010-09-03 07:56:38 -07001423 * Create the connection to vold with a maximum queue of twice the
1424 * amount of containers we'd ever expect to have. This keeps an
1425 * "asec list" from blocking a thread repeatedly.
1426 */
Dianne Hackborn77b987f2014-02-26 16:20:52 -08001427 mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25,
1428 null);
Kenny Root51a573c2012-05-17 13:30:28 -07001429
Kenny Root305bcbf2010-09-03 07:56:38 -07001430 Thread thread = new Thread(mConnector, VOLD_TAG);
San Mehat207e5382010-02-04 20:46:54 -08001431 thread.start();
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -07001432
Kenny Root07714d42011-08-17 17:49:28 -07001433 // Add ourself to the Watchdog monitors if enabled.
1434 if (WATCHDOG_ENABLE) {
1435 Watchdog.getInstance().addMonitor(this);
1436 }
San Mehat207e5382010-02-04 20:46:54 -08001437 }
1438
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001439 public void systemReady() {
1440 mSystemReady = true;
1441 mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget();
1442 }
1443
San Mehat207e5382010-02-04 20:46:54 -08001444 /**
San Mehat4270e1e2010-01-29 05:32:19 -08001445 * Exposed API calls below here
1446 */
1447
1448 public void registerListener(IMountServiceListener listener) {
1449 synchronized (mListeners) {
1450 MountServiceBinderListener bl = new MountServiceBinderListener(listener);
1451 try {
1452 listener.asBinder().linkToDeath(bl, 0);
1453 mListeners.add(bl);
1454 } catch (RemoteException rex) {
San Mehata5078592010-03-25 09:36:54 -07001455 Slog.e(TAG, "Failed to link to listener death");
San Mehat4270e1e2010-01-29 05:32:19 -08001456 }
1457 }
1458 }
1459
1460 public void unregisterListener(IMountServiceListener listener) {
1461 synchronized (mListeners) {
1462 for(MountServiceBinderListener bl : mListeners) {
Niklas Brunlidd64fe0f2013-07-05 08:54:03 +02001463 if (bl.mListener.asBinder() == listener.asBinder()) {
San Mehat4270e1e2010-01-29 05:32:19 -08001464 mListeners.remove(mListeners.indexOf(bl));
Vairavan Srinivasan5c25a2d2012-01-24 08:22:14 -08001465 listener.asBinder().unlinkToDeath(bl, 0);
San Mehat4270e1e2010-01-29 05:32:19 -08001466 return;
1467 }
1468 }
1469 }
1470 }
1471
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -08001472 public void shutdown(final IMountShutdownObserver observer) {
San Mehat4270e1e2010-01-29 05:32:19 -08001473 validatePermission(android.Manifest.permission.SHUTDOWN);
1474
San Mehata5078592010-03-25 09:36:54 -07001475 Slog.i(TAG, "Shutting down");
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001476 synchronized (mVolumesLock) {
Rickard Helldin5fb5df02014-02-07 09:35:04 +01001477 // Get all volumes to be unmounted.
1478 MountShutdownLatch mountShutdownLatch = new MountShutdownLatch(observer,
1479 mVolumeStates.size());
1480
Mike Lockwood7fa24aa2011-03-23 14:52:34 -04001481 for (String path : mVolumeStates.keySet()) {
1482 String state = mVolumeStates.get(path);
San Mehat4270e1e2010-01-29 05:32:19 -08001483
Mike Lockwood7fa24aa2011-03-23 14:52:34 -04001484 if (state.equals(Environment.MEDIA_SHARED)) {
1485 /*
1486 * If the media is currently shared, unshare it.
1487 * XXX: This is still dangerous!. We should not
1488 * be rebooting at *all* if UMS is enabled, since
1489 * the UMS host could have dirty FAT cache entries
1490 * yet to flush.
1491 */
1492 setUsbMassStorageEnabled(false);
1493 } else if (state.equals(Environment.MEDIA_CHECKING)) {
1494 /*
1495 * If the media is being checked, then we need to wait for
1496 * it to complete before being able to proceed.
1497 */
1498 // XXX: @hackbod - Should we disable the ANR timer here?
1499 int retries = 30;
1500 while (state.equals(Environment.MEDIA_CHECKING) && (retries-- >=0)) {
1501 try {
1502 Thread.sleep(1000);
1503 } catch (InterruptedException iex) {
1504 Slog.e(TAG, "Interrupted while waiting for media", iex);
1505 break;
1506 }
1507 state = Environment.getExternalStorageState();
1508 }
1509 if (retries == 0) {
1510 Slog.e(TAG, "Timed out waiting for media to check");
1511 }
San Mehat91c77612010-01-07 10:39:41 -08001512 }
San Mehat91c77612010-01-07 10:39:41 -08001513
Mike Lockwood7fa24aa2011-03-23 14:52:34 -04001514 if (state.equals(Environment.MEDIA_MOUNTED)) {
1515 // Post a unmount message.
Rickard Helldin5fb5df02014-02-07 09:35:04 +01001516 ShutdownCallBack ucb = new ShutdownCallBack(path, mountShutdownLatch);
Mike Lockwood7fa24aa2011-03-23 14:52:34 -04001517 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
1518 } else if (observer != null) {
1519 /*
Rickard Helldin5fb5df02014-02-07 09:35:04 +01001520 * Count down, since nothing will be done. The observer will be
1521 * notified when we are done so shutdown sequence can continue.
Mike Lockwood7fa24aa2011-03-23 14:52:34 -04001522 */
Rickard Helldin5fb5df02014-02-07 09:35:04 +01001523 mountShutdownLatch.countDown();
1524 Slog.i(TAG, "Unmount completed: " + path +
1525 ", result code: " + StorageResultCode.OperationSucceeded);
Mike Lockwood7fa24aa2011-03-23 14:52:34 -04001526 }
Johan Alfven5d0db4d2010-11-09 10:32:25 +01001527 }
San Mehat4270e1e2010-01-29 05:32:19 -08001528 }
1529 }
1530
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001531 private boolean getUmsEnabling() {
1532 synchronized (mListeners) {
1533 return mUmsEnabling;
1534 }
1535 }
1536
1537 private void setUmsEnabling(boolean enable) {
1538 synchronized (mListeners) {
Tony Wufc711252010-08-09 16:49:19 +08001539 mUmsEnabling = enable;
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001540 }
1541 }
1542
San Mehatb1043402010-02-05 08:26:50 -08001543 public boolean isUsbMassStorageConnected() {
San Mehat207e5382010-02-04 20:46:54 -08001544 waitForReady();
San Mehat91c77612010-01-07 10:39:41 -08001545
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001546 if (getUmsEnabling()) {
San Mehatb1043402010-02-05 08:26:50 -08001547 return true;
San Mehat7fd0fee2009-12-17 07:12:23 -08001548 }
Mike Lockwoodecedfdc2011-06-08 15:11:59 -07001549 synchronized (mListeners) {
1550 return mUmsAvailable;
1551 }
San Mehatb1043402010-02-05 08:26:50 -08001552 }
1553
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001554 public void setUsbMassStorageEnabled(boolean enable) {
San Mehatb1043402010-02-05 08:26:50 -08001555 waitForReady();
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001556 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
San Mehatb1043402010-02-05 08:26:50 -08001557
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001558 final StorageVolume primary = getPrimaryPhysicalVolume();
1559 if (primary == null) return;
1560
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001561 // TODO: Add support for multiple share methods
1562
1563 /*
1564 * If the volume is mounted and we're enabling then unmount it
1565 */
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001566 String path = primary.getPath();
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001567 String vs = getVolumeState(path);
1568 String method = "ums";
1569 if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
1570 // Override for isUsbMassStorageEnabled()
1571 setUmsEnabling(enable);
1572 UmsEnableCallBack umscb = new UmsEnableCallBack(path, method, true);
1573 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, umscb));
1574 // Clear override
1575 setUmsEnabling(false);
1576 }
1577 /*
1578 * If we disabled UMS then mount the volume
1579 */
1580 if (!enable) {
1581 doShareUnshareVolume(path, method, enable);
1582 if (doMountVolume(path) != StorageResultCode.OperationSucceeded) {
San Mehata5078592010-03-25 09:36:54 -07001583 Slog.e(TAG, "Failed to remount " + path +
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001584 " after disabling share method " + method);
1585 /*
1586 * Even though the mount failed, the unshare didn't so don't indicate an error.
1587 * The mountVolume() call will have set the storage state and sent the necessary
1588 * broadcasts.
1589 */
1590 }
1591 }
San Mehatb1043402010-02-05 08:26:50 -08001592 }
1593
1594 public boolean isUsbMassStorageEnabled() {
1595 waitForReady();
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001596
1597 final StorageVolume primary = getPrimaryPhysicalVolume();
1598 if (primary != null) {
1599 return doGetVolumeShared(primary.getPath(), "ums");
1600 } else {
1601 return false;
1602 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001603 }
Jason parks9ed98bc2011-01-17 09:58:35 -06001604
San Mehat7fd0fee2009-12-17 07:12:23 -08001605 /**
1606 * @return state of the volume at the specified mount point
1607 */
San Mehat4270e1e2010-01-29 05:32:19 -08001608 public String getVolumeState(String mountPoint) {
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001609 synchronized (mVolumesLock) {
Mike Lockwood7fa24aa2011-03-23 14:52:34 -04001610 String state = mVolumeStates.get(mountPoint);
1611 if (state == null) {
1612 Slog.w(TAG, "getVolumeState(" + mountPoint + "): Unknown volume");
Ken Sumrall18db5c52011-07-14 11:35:06 -07001613 if (SystemProperties.get("vold.encrypt_progress").length() != 0) {
1614 state = Environment.MEDIA_REMOVED;
1615 } else {
1616 throw new IllegalArgumentException();
1617 }
Mike Lockwood7fa24aa2011-03-23 14:52:34 -04001618 }
San Mehat7fd0fee2009-12-17 07:12:23 -08001619
Mike Lockwood7fa24aa2011-03-23 14:52:34 -04001620 return state;
1621 }
San Mehat7fd0fee2009-12-17 07:12:23 -08001622 }
1623
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001624 @Override
Kenny Roote1ff2142010-10-12 11:20:01 -07001625 public boolean isExternalStorageEmulated() {
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001626 return mEmulatedTemplate != null;
Kenny Roote1ff2142010-10-12 11:20:01 -07001627 }
1628
San Mehat4270e1e2010-01-29 05:32:19 -08001629 public int mountVolume(String path) {
1630 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
San Mehat4270e1e2010-01-29 05:32:19 -08001631
San Mehat207e5382010-02-04 20:46:54 -08001632 waitForReady();
1633 return doMountVolume(path);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001634 }
1635
Ben Komalo13c71972011-09-07 16:35:56 -07001636 public void unmountVolume(String path, boolean force, boolean removeEncryption) {
San Mehat4270e1e2010-01-29 05:32:19 -08001637 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
San Mehat207e5382010-02-04 20:46:54 -08001638 waitForReady();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001639
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -08001640 String volState = getVolumeState(path);
Ben Komalo13c71972011-09-07 16:35:56 -07001641 if (DEBUG_UNMOUNT) {
1642 Slog.i(TAG, "Unmounting " + path
1643 + " force = " + force
1644 + " removeEncryption = " + removeEncryption);
1645 }
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -08001646 if (Environment.MEDIA_UNMOUNTED.equals(volState) ||
1647 Environment.MEDIA_REMOVED.equals(volState) ||
1648 Environment.MEDIA_SHARED.equals(volState) ||
1649 Environment.MEDIA_UNMOUNTABLE.equals(volState)) {
1650 // Media already unmounted or cannot be unmounted.
1651 // TODO return valid return code when adding observer call back.
1652 return;
1653 }
Ben Komalo13c71972011-09-07 16:35:56 -07001654 UnmountCallBack ucb = new UnmountCallBack(path, force, removeEncryption);
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -08001655 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001656 }
1657
San Mehat4270e1e2010-01-29 05:32:19 -08001658 public int formatVolume(String path) {
1659 validatePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
San Mehat207e5382010-02-04 20:46:54 -08001660 waitForReady();
San Mehat5b77dab2010-01-26 13:28:50 -08001661
San Mehat207e5382010-02-04 20:46:54 -08001662 return doFormatVolume(path);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001663 }
1664
Mike Lockwoodecedfdc2011-06-08 15:11:59 -07001665 public int[] getStorageUsers(String path) {
San Mehatc1b4ce92010-02-16 17:13:03 -08001666 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1667 waitForReady();
1668 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001669 final String[] r = NativeDaemonEvent.filterMessageList(
1670 mConnector.executeForList("storage", "users", path),
1671 VoldResponseCode.StorageUsersListResult);
1672
San Mehatc1b4ce92010-02-16 17:13:03 -08001673 // FMT: <pid> <process name>
1674 int[] data = new int[r.length];
1675 for (int i = 0; i < r.length; i++) {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001676 String[] tok = r[i].split(" ");
San Mehatc1b4ce92010-02-16 17:13:03 -08001677 try {
1678 data[i] = Integer.parseInt(tok[0]);
1679 } catch (NumberFormatException nfe) {
San Mehata5078592010-03-25 09:36:54 -07001680 Slog.e(TAG, String.format("Error parsing pid %s", tok[0]));
San Mehatc1b4ce92010-02-16 17:13:03 -08001681 return new int[0];
1682 }
1683 }
1684 return data;
1685 } catch (NativeDaemonConnectorException e) {
San Mehata5078592010-03-25 09:36:54 -07001686 Slog.e(TAG, "Failed to retrieve storage users list", e);
San Mehatc1b4ce92010-02-16 17:13:03 -08001687 return new int[0];
1688 }
1689 }
1690
San Mehatb1043402010-02-05 08:26:50 -08001691 private void warnOnNotMounted() {
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001692 final StorageVolume primary = getPrimaryPhysicalVolume();
Jeff Sharkey32ee8312012-09-30 13:21:31 -07001693 if (primary != null) {
1694 boolean mounted = false;
1695 try {
1696 mounted = Environment.MEDIA_MOUNTED.equals(getVolumeState(primary.getPath()));
Jeff Sharkey9ae62f52013-03-26 10:29:01 -07001697 } catch (IllegalArgumentException e) {
Jeff Sharkey32ee8312012-09-30 13:21:31 -07001698 }
1699
1700 if (!mounted) {
1701 Slog.w(TAG, "getSecureContainerList() called when storage not mounted");
1702 }
San Mehatb1043402010-02-05 08:26:50 -08001703 }
1704 }
1705
San Mehat4270e1e2010-01-29 05:32:19 -08001706 public String[] getSecureContainerList() {
1707 validatePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat207e5382010-02-04 20:46:54 -08001708 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001709 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001710
San Mehat4270e1e2010-01-29 05:32:19 -08001711 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001712 return NativeDaemonEvent.filterMessageList(
1713 mConnector.executeForList("asec", "list"), VoldResponseCode.AsecListResult);
San Mehat4270e1e2010-01-29 05:32:19 -08001714 } catch (NativeDaemonConnectorException e) {
1715 return new String[0];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001716 }
1717 }
San Mehat36972292010-01-06 11:06:32 -08001718
Kenny Root6dceb882012-04-12 14:23:49 -07001719 public int createSecureContainer(String id, int sizeMb, String fstype, String key,
1720 int ownerUid, boolean external) {
San Mehat4270e1e2010-01-29 05:32:19 -08001721 validatePermission(android.Manifest.permission.ASEC_CREATE);
San Mehat207e5382010-02-04 20:46:54 -08001722 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001723 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001724
San Mehatb1043402010-02-05 08:26:50 -08001725 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001726 try {
Jeff Sharkey56cd6462013-06-07 15:09:15 -07001727 mConnector.execute("asec", "create", id, sizeMb, fstype, new SensitiveArg(key),
1728 ownerUid, external ? "1" : "0");
San Mehat4270e1e2010-01-29 05:32:19 -08001729 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001730 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001731 }
San Mehata181b212010-02-11 06:50:20 -08001732
1733 if (rc == StorageResultCode.OperationSucceeded) {
1734 synchronized (mAsecMountSet) {
1735 mAsecMountSet.add(id);
1736 }
1737 }
San Mehat4270e1e2010-01-29 05:32:19 -08001738 return rc;
San Mehat36972292010-01-06 11:06:32 -08001739 }
1740
San Mehat4270e1e2010-01-29 05:32:19 -08001741 public int finalizeSecureContainer(String id) {
1742 validatePermission(android.Manifest.permission.ASEC_CREATE);
San Mehatb1043402010-02-05 08:26:50 -08001743 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001744
San Mehatb1043402010-02-05 08:26:50 -08001745 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001746 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001747 mConnector.execute("asec", "finalize", id);
San Mehata181b212010-02-11 06:50:20 -08001748 /*
1749 * Finalization does a remount, so no need
1750 * to update mAsecMountSet
1751 */
San Mehat4270e1e2010-01-29 05:32:19 -08001752 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001753 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001754 }
San Mehat4270e1e2010-01-29 05:32:19 -08001755 return rc;
San Mehat36972292010-01-06 11:06:32 -08001756 }
1757
Kenny Root6dceb882012-04-12 14:23:49 -07001758 public int fixPermissionsSecureContainer(String id, int gid, String filename) {
1759 validatePermission(android.Manifest.permission.ASEC_CREATE);
1760 warnOnNotMounted();
1761
1762 int rc = StorageResultCode.OperationSucceeded;
1763 try {
1764 mConnector.execute("asec", "fixperms", id, gid, filename);
1765 /*
1766 * Fix permissions does a remount, so no need to update
1767 * mAsecMountSet
1768 */
1769 } catch (NativeDaemonConnectorException e) {
1770 rc = StorageResultCode.OperationFailedInternalError;
1771 }
1772 return rc;
1773 }
1774
San Mehatd9709982010-02-18 11:43:03 -08001775 public int destroySecureContainer(String id, boolean force) {
San Mehat4270e1e2010-01-29 05:32:19 -08001776 validatePermission(android.Manifest.permission.ASEC_DESTROY);
San Mehat207e5382010-02-04 20:46:54 -08001777 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001778 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001779
Kenny Rootaa485402010-09-14 14:49:41 -07001780 /*
1781 * Force a GC to make sure AssetManagers in other threads of the
1782 * system_server are cleaned up. We have to do this since AssetManager
1783 * instances are kept as a WeakReference and it's possible we have files
1784 * open on the external storage.
1785 */
1786 Runtime.getRuntime().gc();
1787
San Mehatb1043402010-02-05 08:26:50 -08001788 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001789 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001790 final Command cmd = new Command("asec", "destroy", id);
1791 if (force) {
1792 cmd.appendArg("force");
1793 }
1794 mConnector.execute(cmd);
San Mehat4270e1e2010-01-29 05:32:19 -08001795 } catch (NativeDaemonConnectorException e) {
San Mehatd9709982010-02-18 11:43:03 -08001796 int code = e.getCode();
1797 if (code == VoldResponseCode.OpFailedStorageBusy) {
1798 rc = StorageResultCode.OperationFailedStorageBusy;
1799 } else {
1800 rc = StorageResultCode.OperationFailedInternalError;
1801 }
San Mehat02735bc2010-01-26 15:18:08 -08001802 }
San Mehata181b212010-02-11 06:50:20 -08001803
1804 if (rc == StorageResultCode.OperationSucceeded) {
1805 synchronized (mAsecMountSet) {
1806 if (mAsecMountSet.contains(id)) {
1807 mAsecMountSet.remove(id);
1808 }
1809 }
1810 }
1811
San Mehat4270e1e2010-01-29 05:32:19 -08001812 return rc;
San Mehat36972292010-01-06 11:06:32 -08001813 }
Jason parks9ed98bc2011-01-17 09:58:35 -06001814
San Mehat4270e1e2010-01-29 05:32:19 -08001815 public int mountSecureContainer(String id, String key, int ownerUid) {
1816 validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
San Mehat207e5382010-02-04 20:46:54 -08001817 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001818 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001819
San Mehata181b212010-02-11 06:50:20 -08001820 synchronized (mAsecMountSet) {
1821 if (mAsecMountSet.contains(id)) {
1822 return StorageResultCode.OperationFailedStorageMounted;
1823 }
1824 }
1825
San Mehatb1043402010-02-05 08:26:50 -08001826 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001827 try {
Jeff Sharkey56cd6462013-06-07 15:09:15 -07001828 mConnector.execute("asec", "mount", id, new SensitiveArg(key), ownerUid);
San Mehat4270e1e2010-01-29 05:32:19 -08001829 } catch (NativeDaemonConnectorException e) {
Kenny Rootf0304622010-03-19 19:20:42 -07001830 int code = e.getCode();
1831 if (code != VoldResponseCode.OpFailedStorageBusy) {
1832 rc = StorageResultCode.OperationFailedInternalError;
1833 }
San Mehat02735bc2010-01-26 15:18:08 -08001834 }
San Mehat6cdd9c02010-02-09 14:45:20 -08001835
1836 if (rc == StorageResultCode.OperationSucceeded) {
1837 synchronized (mAsecMountSet) {
1838 mAsecMountSet.add(id);
1839 }
1840 }
San Mehat4270e1e2010-01-29 05:32:19 -08001841 return rc;
San Mehat36972292010-01-06 11:06:32 -08001842 }
1843
San Mehatd9709982010-02-18 11:43:03 -08001844 public int unmountSecureContainer(String id, boolean force) {
San Mehat4270e1e2010-01-29 05:32:19 -08001845 validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
San Mehat207e5382010-02-04 20:46:54 -08001846 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001847 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001848
San Mehat6cdd9c02010-02-09 14:45:20 -08001849 synchronized (mAsecMountSet) {
1850 if (!mAsecMountSet.contains(id)) {
San Mehata181b212010-02-11 06:50:20 -08001851 return StorageResultCode.OperationFailedStorageNotMounted;
San Mehat6cdd9c02010-02-09 14:45:20 -08001852 }
1853 }
1854
Kenny Rootaa485402010-09-14 14:49:41 -07001855 /*
1856 * Force a GC to make sure AssetManagers in other threads of the
1857 * system_server are cleaned up. We have to do this since AssetManager
1858 * instances are kept as a WeakReference and it's possible we have files
1859 * open on the external storage.
1860 */
1861 Runtime.getRuntime().gc();
1862
San Mehatb1043402010-02-05 08:26:50 -08001863 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001864 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001865 final Command cmd = new Command("asec", "unmount", id);
1866 if (force) {
1867 cmd.appendArg("force");
1868 }
1869 mConnector.execute(cmd);
San Mehat4270e1e2010-01-29 05:32:19 -08001870 } catch (NativeDaemonConnectorException e) {
San Mehatd9709982010-02-18 11:43:03 -08001871 int code = e.getCode();
1872 if (code == VoldResponseCode.OpFailedStorageBusy) {
1873 rc = StorageResultCode.OperationFailedStorageBusy;
1874 } else {
1875 rc = StorageResultCode.OperationFailedInternalError;
1876 }
San Mehat02735bc2010-01-26 15:18:08 -08001877 }
San Mehat6cdd9c02010-02-09 14:45:20 -08001878
1879 if (rc == StorageResultCode.OperationSucceeded) {
1880 synchronized (mAsecMountSet) {
1881 mAsecMountSet.remove(id);
1882 }
1883 }
San Mehat4270e1e2010-01-29 05:32:19 -08001884 return rc;
San Mehat9dba7092010-01-18 06:47:41 -08001885 }
1886
San Mehat6cdd9c02010-02-09 14:45:20 -08001887 public boolean isSecureContainerMounted(String id) {
1888 validatePermission(android.Manifest.permission.ASEC_ACCESS);
1889 waitForReady();
1890 warnOnNotMounted();
1891
1892 synchronized (mAsecMountSet) {
1893 return mAsecMountSet.contains(id);
1894 }
1895 }
1896
San Mehat4270e1e2010-01-29 05:32:19 -08001897 public int renameSecureContainer(String oldId, String newId) {
1898 validatePermission(android.Manifest.permission.ASEC_RENAME);
San Mehat207e5382010-02-04 20:46:54 -08001899 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001900 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001901
San Mehata181b212010-02-11 06:50:20 -08001902 synchronized (mAsecMountSet) {
San Mehat85451ee2010-02-24 08:54:18 -08001903 /*
Jason parks9ed98bc2011-01-17 09:58:35 -06001904 * Because a mounted container has active internal state which cannot be
San Mehat85451ee2010-02-24 08:54:18 -08001905 * changed while active, we must ensure both ids are not currently mounted.
1906 */
1907 if (mAsecMountSet.contains(oldId) || mAsecMountSet.contains(newId)) {
San Mehata181b212010-02-11 06:50:20 -08001908 return StorageResultCode.OperationFailedStorageMounted;
1909 }
1910 }
1911
San Mehatb1043402010-02-05 08:26:50 -08001912 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001913 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001914 mConnector.execute("asec", "rename", oldId, newId);
San Mehat4270e1e2010-01-29 05:32:19 -08001915 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001916 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001917 }
San Mehata181b212010-02-11 06:50:20 -08001918
San Mehat4270e1e2010-01-29 05:32:19 -08001919 return rc;
San Mehat45f61042010-01-23 08:12:43 -08001920 }
1921
San Mehat4270e1e2010-01-29 05:32:19 -08001922 public String getSecureContainerPath(String id) {
1923 validatePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat207e5382010-02-04 20:46:54 -08001924 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001925 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001926
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001927 final NativeDaemonEvent event;
San Mehat2d66cef2010-03-23 11:12:52 -07001928 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001929 event = mConnector.execute("asec", "path", id);
1930 event.checkCode(VoldResponseCode.AsecPathResult);
1931 return event.getMessage();
San Mehat2d66cef2010-03-23 11:12:52 -07001932 } catch (NativeDaemonConnectorException e) {
1933 int code = e.getCode();
1934 if (code == VoldResponseCode.OpFailedStorageNotFound) {
Fredrik Helmera20c8ef2011-02-09 16:16:10 +01001935 Slog.i(TAG, String.format("Container '%s' not found", id));
1936 return null;
San Mehat22dd86e2010-01-12 12:21:18 -08001937 } else {
San Mehat2d66cef2010-03-23 11:12:52 -07001938 throw new IllegalStateException(String.format("Unexpected response code %d", code));
San Mehat22dd86e2010-01-12 12:21:18 -08001939 }
1940 }
San Mehat22dd86e2010-01-12 12:21:18 -08001941 }
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -07001942
Dianne Hackborn292f8bc2011-06-27 16:27:41 -07001943 public String getSecureContainerFilesystemPath(String id) {
1944 validatePermission(android.Manifest.permission.ASEC_ACCESS);
1945 waitForReady();
1946 warnOnNotMounted();
1947
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001948 final NativeDaemonEvent event;
Dianne Hackborn292f8bc2011-06-27 16:27:41 -07001949 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001950 event = mConnector.execute("asec", "fspath", id);
1951 event.checkCode(VoldResponseCode.AsecPathResult);
1952 return event.getMessage();
Dianne Hackborn292f8bc2011-06-27 16:27:41 -07001953 } catch (NativeDaemonConnectorException e) {
1954 int code = e.getCode();
1955 if (code == VoldResponseCode.OpFailedStorageNotFound) {
1956 Slog.i(TAG, String.format("Container '%s' not found", id));
1957 return null;
1958 } else {
1959 throw new IllegalStateException(String.format("Unexpected response code %d", code));
1960 }
1961 }
1962 }
1963
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -07001964 public void finishMediaUpdate() {
1965 mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
1966 }
Kenny Root02c87302010-07-01 08:10:18 -07001967
Kenny Roota02b8b02010-08-05 16:14:17 -07001968 private boolean isUidOwnerOfPackageOrSystem(String packageName, int callerUid) {
1969 if (callerUid == android.os.Process.SYSTEM_UID) {
1970 return true;
1971 }
1972
Kenny Root02c87302010-07-01 08:10:18 -07001973 if (packageName == null) {
1974 return false;
1975 }
1976
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07001977 final int packageUid = mPms.getPackageUid(packageName, UserHandle.getUserId(callerUid));
Kenny Root02c87302010-07-01 08:10:18 -07001978
1979 if (DEBUG_OBB) {
1980 Slog.d(TAG, "packageName = " + packageName + ", packageUid = " +
1981 packageUid + ", callerUid = " + callerUid);
1982 }
1983
1984 return callerUid == packageUid;
1985 }
1986
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001987 public String getMountedObbPath(String rawPath) {
1988 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
Kenny Rootaf9d6672010-10-08 09:21:39 -07001989
Kenny Root02c87302010-07-01 08:10:18 -07001990 waitForReady();
1991 warnOnNotMounted();
1992
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001993 final ObbState state;
1994 synchronized (mObbPathToStateMap) {
1995 state = mObbPathToStateMap.get(rawPath);
1996 }
1997 if (state == null) {
1998 Slog.w(TAG, "Failed to find OBB mounted at " + rawPath);
1999 return null;
2000 }
2001
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002002 final NativeDaemonEvent event;
Kenny Root02c87302010-07-01 08:10:18 -07002003 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002004 event = mConnector.execute("obb", "path", state.voldPath);
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002005 event.checkCode(VoldResponseCode.AsecPathResult);
2006 return event.getMessage();
Kenny Root02c87302010-07-01 08:10:18 -07002007 } catch (NativeDaemonConnectorException e) {
2008 int code = e.getCode();
2009 if (code == VoldResponseCode.OpFailedStorageNotFound) {
Kenny Roota02b8b02010-08-05 16:14:17 -07002010 return null;
Kenny Root02c87302010-07-01 08:10:18 -07002011 } else {
2012 throw new IllegalStateException(String.format("Unexpected response code %d", code));
2013 }
2014 }
2015 }
2016
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002017 @Override
2018 public boolean isObbMounted(String rawPath) {
2019 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
Kenny Rootaf9d6672010-10-08 09:21:39 -07002020 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002021 return mObbPathToStateMap.containsKey(rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002022 }
Kenny Root02c87302010-07-01 08:10:18 -07002023 }
2024
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002025 @Override
2026 public void mountObb(
2027 String rawPath, String canonicalPath, String key, IObbActionListener token, int nonce) {
2028 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
2029 Preconditions.checkNotNull(canonicalPath, "canonicalPath cannot be null");
2030 Preconditions.checkNotNull(token, "token cannot be null");
Kenny Rootaf9d6672010-10-08 09:21:39 -07002031
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002032 final int callingUid = Binder.getCallingUid();
2033 final ObbState obbState = new ObbState(rawPath, canonicalPath, callingUid, token, nonce);
2034 final ObbAction action = new MountObbAction(obbState, key, callingUid);
Kenny Roota02b8b02010-08-05 16:14:17 -07002035 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
2036
2037 if (DEBUG_OBB)
2038 Slog.i(TAG, "Send to OBB handler: " + action.toString());
Kenny Root02c87302010-07-01 08:10:18 -07002039 }
2040
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002041 @Override
2042 public void unmountObb(String rawPath, boolean force, IObbActionListener token, int nonce) {
2043 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
2044
2045 final ObbState existingState;
2046 synchronized (mObbPathToStateMap) {
2047 existingState = mObbPathToStateMap.get(rawPath);
Kenny Rootf1121dc2010-09-29 07:30:53 -07002048 }
2049
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002050 if (existingState != null) {
2051 // TODO: separate state object from request data
2052 final int callingUid = Binder.getCallingUid();
2053 final ObbState newState = new ObbState(
2054 rawPath, existingState.canonicalPath, callingUid, token, nonce);
2055 final ObbAction action = new UnmountObbAction(newState, force);
2056 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
Kenny Root02c87302010-07-01 08:10:18 -07002057
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002058 if (DEBUG_OBB)
2059 Slog.i(TAG, "Send to OBB handler: " + action.toString());
2060 } else {
2061 Slog.w(TAG, "Unknown OBB mount at " + rawPath);
2062 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002063 }
2064
Ben Komalo444eca22011-09-01 15:17:44 -07002065 @Override
2066 public int getEncryptionState() {
2067 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2068 "no permission to access the crypt keeper");
2069
2070 waitForReady();
2071
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002072 final NativeDaemonEvent event;
Ben Komalo444eca22011-09-01 15:17:44 -07002073 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002074 event = mConnector.execute("cryptfs", "cryptocomplete");
2075 return Integer.parseInt(event.getMessage());
Ben Komalo444eca22011-09-01 15:17:44 -07002076 } catch (NumberFormatException e) {
2077 // Bad result - unexpected.
2078 Slog.w(TAG, "Unable to parse result from cryptfs cryptocomplete");
2079 return ENCRYPTION_STATE_ERROR_UNKNOWN;
2080 } catch (NativeDaemonConnectorException e) {
2081 // Something bad happened.
2082 Slog.w(TAG, "Error in communicating with cryptfs in validating");
2083 return ENCRYPTION_STATE_ERROR_UNKNOWN;
2084 }
2085 }
2086
Paul Lawrence8e397362014-01-27 15:22:30 -08002087 private String toHex(String password) {
2088 if (password == null) {
Paul Lawrence945490c2014-03-27 16:37:28 +00002089 return new String();
Paul Lawrence8e397362014-01-27 15:22:30 -08002090 }
2091 byte[] bytes = password.getBytes(StandardCharsets.UTF_8);
2092 return new String(Hex.encodeHex(bytes));
2093 }
2094
Paul Lawrence945490c2014-03-27 16:37:28 +00002095 private String fromHex(String hexPassword) {
2096 if (hexPassword == null) {
2097 return null;
2098 }
2099
2100 try {
2101 byte[] bytes = Hex.decodeHex(hexPassword.toCharArray());
2102 return new String(bytes, StandardCharsets.UTF_8);
2103 } catch (DecoderException e) {
2104 return null;
2105 }
2106 }
2107
Ben Komalo444eca22011-09-01 15:17:44 -07002108 @Override
Jason parks5af0b912010-11-29 09:05:25 -06002109 public int decryptStorage(String password) {
Jason parksf7b3cd42011-01-27 09:28:25 -06002110 if (TextUtils.isEmpty(password)) {
2111 throw new IllegalArgumentException("password cannot be empty");
Jason parks5af0b912010-11-29 09:05:25 -06002112 }
2113
Jason parks8888c592011-01-20 22:46:41 -06002114 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2115 "no permission to access the crypt keeper");
Jason parks5af0b912010-11-29 09:05:25 -06002116
2117 waitForReady();
2118
2119 if (DEBUG_EVENTS) {
2120 Slog.i(TAG, "decrypting storage...");
2121 }
2122
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002123 final NativeDaemonEvent event;
Jason parks5af0b912010-11-29 09:05:25 -06002124 try {
Paul Lawrence8e397362014-01-27 15:22:30 -08002125 event = mConnector.execute("cryptfs", "checkpw", new SensitiveArg(toHex(password)));
Jason parks9ed98bc2011-01-17 09:58:35 -06002126
Fredrik Roubertda6aedf2011-12-20 17:34:43 +01002127 final int code = Integer.parseInt(event.getMessage());
Jason parks9ed98bc2011-01-17 09:58:35 -06002128 if (code == 0) {
2129 // Decrypt was successful. Post a delayed message before restarting in order
2130 // to let the UI to clear itself
2131 mHandler.postDelayed(new Runnable() {
2132 public void run() {
Jeff Sharkey31c6e482011-11-18 17:09:01 -08002133 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002134 mConnector.execute("cryptfs", "restart");
Jeff Sharkey31c6e482011-11-18 17:09:01 -08002135 } catch (NativeDaemonConnectorException e) {
2136 Slog.e(TAG, "problem executing in background", e);
2137 }
Jason parks9ed98bc2011-01-17 09:58:35 -06002138 }
Jason parksf7b3cd42011-01-27 09:28:25 -06002139 }, 1000); // 1 second
Jason parks9ed98bc2011-01-17 09:58:35 -06002140 }
2141
2142 return code;
Jason parks5af0b912010-11-29 09:05:25 -06002143 } catch (NativeDaemonConnectorException e) {
2144 // Decryption failed
2145 return e.getCode();
2146 }
Jason parks5af0b912010-11-29 09:05:25 -06002147 }
2148
Paul Lawrence46791e72014-04-03 09:10:26 -07002149 public int encryptStorage(int type, String password) {
2150 if (TextUtils.isEmpty(password) && type != StorageManager.CRYPT_TYPE_DEFAULT) {
Jason parksf7b3cd42011-01-27 09:28:25 -06002151 throw new IllegalArgumentException("password cannot be empty");
Jason parks56aa5322011-01-07 09:01:15 -06002152 }
2153
Jason parks8888c592011-01-20 22:46:41 -06002154 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2155 "no permission to access the crypt keeper");
Jason parks56aa5322011-01-07 09:01:15 -06002156
2157 waitForReady();
2158
2159 if (DEBUG_EVENTS) {
Jason parks8888c592011-01-20 22:46:41 -06002160 Slog.i(TAG, "encrypting storage...");
Jason parks56aa5322011-01-07 09:01:15 -06002161 }
2162
2163 try {
Paul Lawrence46791e72014-04-03 09:10:26 -07002164 mConnector.execute("cryptfs", "enablecrypto", "inplace", CRYPTO_TYPES[type],
Paul Lawrence8e397362014-01-27 15:22:30 -08002165 new SensitiveArg(toHex(password)));
Jason parks56aa5322011-01-07 09:01:15 -06002166 } catch (NativeDaemonConnectorException e) {
2167 // Encryption failed
2168 return e.getCode();
2169 }
2170
2171 return 0;
2172 }
2173
Paul Lawrence8e397362014-01-27 15:22:30 -08002174 /** Set the password for encrypting the master key.
2175 * @param type One of the CRYPTO_TYPE_XXX consts defined in StorageManager.
2176 * @param password The password to set.
2177 */
2178 public int changeEncryptionPassword(int type, String password) {
Jason parksf7b3cd42011-01-27 09:28:25 -06002179 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2180 "no permission to access the crypt keeper");
2181
2182 waitForReady();
2183
2184 if (DEBUG_EVENTS) {
2185 Slog.i(TAG, "changing encryption password...");
2186 }
2187
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002188 final NativeDaemonEvent event;
Jason parksf7b3cd42011-01-27 09:28:25 -06002189 try {
Paul Lawrence8e397362014-01-27 15:22:30 -08002190 event = mConnector.execute("cryptfs", "changepw", CRYPTO_TYPES[type],
2191 new SensitiveArg(toHex(password)));
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002192 return Integer.parseInt(event.getMessage());
Jason parksf7b3cd42011-01-27 09:28:25 -06002193 } catch (NativeDaemonConnectorException e) {
2194 // Encryption failed
2195 return e.getCode();
2196 }
2197 }
2198
Christopher Tate32418be2011-10-10 13:51:12 -07002199 /**
2200 * Validate a user-supplied password string with cryptfs
2201 */
2202 @Override
2203 public int verifyEncryptionPassword(String password) throws RemoteException {
2204 // Only the system process is permitted to validate passwords
2205 if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
2206 throw new SecurityException("no permission to access the crypt keeper");
2207 }
2208
2209 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2210 "no permission to access the crypt keeper");
2211
2212 if (TextUtils.isEmpty(password)) {
2213 throw new IllegalArgumentException("password cannot be empty");
2214 }
2215
2216 waitForReady();
2217
2218 if (DEBUG_EVENTS) {
2219 Slog.i(TAG, "validating encryption password...");
2220 }
2221
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002222 final NativeDaemonEvent event;
Christopher Tate32418be2011-10-10 13:51:12 -07002223 try {
Paul Lawrence8e397362014-01-27 15:22:30 -08002224 event = mConnector.execute("cryptfs", "verifypw", new SensitiveArg(toHex(password)));
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002225 Slog.i(TAG, "cryptfs verifypw => " + event.getMessage());
2226 return Integer.parseInt(event.getMessage());
Christopher Tate32418be2011-10-10 13:51:12 -07002227 } catch (NativeDaemonConnectorException e) {
2228 // Encryption failed
2229 return e.getCode();
2230 }
2231 }
2232
Paul Lawrence8e397362014-01-27 15:22:30 -08002233 /**
2234 * Get the type of encryption used to encrypt the master key.
2235 * @return The type, one of the CRYPT_TYPE_XXX consts from StorageManager.
2236 */
2237 @Override
2238 public int getPasswordType() throws RemoteException {
2239
2240 waitForReady();
2241
2242 final NativeDaemonEvent event;
2243 try {
2244 event = mConnector.execute("cryptfs", "getpwtype");
2245 for (int i = 0; i < CRYPTO_TYPES.length; ++i) {
2246 if (CRYPTO_TYPES[i].equals(event.getMessage()))
2247 return i;
2248 }
2249
2250 throw new IllegalStateException("unexpected return from cryptfs");
2251 } catch (NativeDaemonConnectorException e) {
2252 throw e.rethrowAsParcelableException();
2253 }
2254 }
2255
Paul Lawrencee51dcf92014-03-18 10:56:00 -07002256 /**
2257 * Set a field in the crypto header.
2258 * @param field field to set
2259 * @param contents contents to set in field
2260 */
2261 @Override
2262 public void setField(String field, String contents) throws RemoteException {
2263
2264 waitForReady();
2265
2266 final NativeDaemonEvent event;
2267 try {
2268 event = mConnector.execute("cryptfs", "setfield", field, contents);
2269 } catch (NativeDaemonConnectorException e) {
2270 throw e.rethrowAsParcelableException();
2271 }
2272 }
2273
2274 /**
2275 * Gets a field from the crypto header.
2276 * @param field field to get
2277 * @return contents of field
2278 */
2279 @Override
2280 public String getField(String field) throws RemoteException {
2281
2282 waitForReady();
2283
2284 final NativeDaemonEvent event;
2285 try {
2286 final String[] contents = NativeDaemonEvent.filterMessageList(
2287 mConnector.executeForList("cryptfs", "getfield", field),
2288 VoldResponseCode.CryptfsGetfieldResult);
2289 String result = new String();
2290 for (String content : contents) {
2291 result += content;
2292 }
2293 return result;
2294 } catch (NativeDaemonConnectorException e) {
2295 throw e.rethrowAsParcelableException();
2296 }
2297 }
2298
Jeff Sharkeyb049e212012-09-07 23:16:01 -07002299 @Override
Paul Lawrence945490c2014-03-27 16:37:28 +00002300 public String getPassword() throws RemoteException {
2301 if (!isReady()) {
2302 return new String();
2303 }
2304
2305 final NativeDaemonEvent event;
2306 try {
2307 event = mConnector.execute("cryptfs", "getpw");
2308 return fromHex(event.getMessage());
2309 } catch (NativeDaemonConnectorException e) {
2310 throw e.rethrowAsParcelableException();
2311 }
2312 }
2313
2314 @Override
2315 public void clearPassword() throws RemoteException {
2316 if (!isReady()) {
2317 return;
2318 }
2319
2320 final NativeDaemonEvent event;
2321 try {
2322 event = mConnector.execute("cryptfs", "clearpw");
2323 } catch (NativeDaemonConnectorException e) {
2324 throw e.rethrowAsParcelableException();
2325 }
2326 }
2327
2328 @Override
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002329 public int mkdirs(String callingPkg, String appPath) {
2330 final int userId = UserHandle.getUserId(Binder.getCallingUid());
2331 final UserEnvironment userEnv = new UserEnvironment(userId);
2332
2333 // Validate that reported package name belongs to caller
2334 final AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(
2335 Context.APP_OPS_SERVICE);
2336 appOps.checkPackage(Binder.getCallingUid(), callingPkg);
2337
2338 try {
2339 appPath = new File(appPath).getCanonicalPath();
2340 } catch (IOException e) {
2341 Slog.e(TAG, "Failed to resolve " + appPath + ": " + e);
2342 return -1;
2343 }
2344
Jeff Sharkey5786a272013-10-02 12:50:34 -07002345 if (!appPath.endsWith("/")) {
2346 appPath = appPath + "/";
2347 }
2348
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002349 // Try translating the app path into a vold path, but require that it
2350 // belong to the calling package.
2351 String voldPath = maybeTranslatePathForVold(appPath,
2352 userEnv.buildExternalStorageAppDataDirs(callingPkg),
2353 userEnv.buildExternalStorageAppDataDirsForVold(callingPkg));
2354 if (voldPath != null) {
2355 try {
2356 mConnector.execute("volume", "mkdirs", voldPath);
2357 return 0;
2358 } catch (NativeDaemonConnectorException e) {
2359 return e.getCode();
2360 }
2361 }
2362
2363 voldPath = maybeTranslatePathForVold(appPath,
2364 userEnv.buildExternalStorageAppObbDirs(callingPkg),
2365 userEnv.buildExternalStorageAppObbDirsForVold(callingPkg));
2366 if (voldPath != null) {
2367 try {
2368 mConnector.execute("volume", "mkdirs", voldPath);
2369 return 0;
2370 } catch (NativeDaemonConnectorException e) {
2371 return e.getCode();
2372 }
2373 }
2374
2375 throw new SecurityException("Invalid mkdirs path: " + appPath);
2376 }
2377
2378 /**
2379 * Translate the given path from an app-visible path to a vold-visible path,
2380 * but only if it's under the given whitelisted paths.
2381 *
2382 * @param path a canonicalized app-visible path.
2383 * @param appPaths list of app-visible paths that are allowed.
2384 * @param voldPaths list of vold-visible paths directly corresponding to the
2385 * allowed app-visible paths argument.
2386 * @return a vold-visible path representing the original path, or
2387 * {@code null} if the given path didn't have an app-to-vold
2388 * mapping.
2389 */
2390 @VisibleForTesting
2391 public static String maybeTranslatePathForVold(
2392 String path, File[] appPaths, File[] voldPaths) {
2393 if (appPaths.length != voldPaths.length) {
2394 throw new IllegalStateException("Paths must be 1:1 mapping");
2395 }
2396
2397 for (int i = 0; i < appPaths.length; i++) {
Jeff Sharkey5786a272013-10-02 12:50:34 -07002398 final String appPath = appPaths[i].getAbsolutePath() + "/";
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002399 if (path.startsWith(appPath)) {
Jeff Sharkey5786a272013-10-02 12:50:34 -07002400 path = new File(voldPaths[i], path.substring(appPath.length()))
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002401 .getAbsolutePath();
2402 if (!path.endsWith("/")) {
2403 path = path + "/";
2404 }
2405 return path;
2406 }
2407 }
2408 return null;
2409 }
2410
2411 @Override
Jeff Sharkeyb049e212012-09-07 23:16:01 -07002412 public StorageVolume[] getVolumeList() {
2413 final int callingUserId = UserHandle.getCallingUserId();
2414 final boolean accessAll = (mContext.checkPermission(
2415 android.Manifest.permission.ACCESS_ALL_EXTERNAL_STORAGE,
2416 Binder.getCallingPid(), Binder.getCallingUid()) == PERMISSION_GRANTED);
2417
2418 synchronized (mVolumesLock) {
2419 final ArrayList<StorageVolume> filtered = Lists.newArrayList();
2420 for (StorageVolume volume : mVolumes) {
2421 final UserHandle owner = volume.getOwner();
2422 final boolean ownerMatch = owner == null || owner.getIdentifier() == callingUserId;
2423 if (accessAll || ownerMatch) {
2424 filtered.add(volume);
2425 }
Mike Lockwood8fa5f802011-03-24 08:12:30 -07002426 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -07002427 return filtered.toArray(new StorageVolume[filtered.size()]);
Mike Lockwood8fa5f802011-03-24 08:12:30 -07002428 }
2429 }
2430
Kenny Rootaf9d6672010-10-08 09:21:39 -07002431 private void addObbStateLocked(ObbState obbState) throws RemoteException {
2432 final IBinder binder = obbState.getBinder();
2433 List<ObbState> obbStates = mObbMounts.get(binder);
Kenny Root5919ac62010-10-05 09:49:40 -07002434
Kenny Rootaf9d6672010-10-08 09:21:39 -07002435 if (obbStates == null) {
2436 obbStates = new ArrayList<ObbState>();
2437 mObbMounts.put(binder, obbStates);
2438 } else {
2439 for (final ObbState o : obbStates) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002440 if (o.rawPath.equals(obbState.rawPath)) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002441 throw new IllegalStateException("Attempt to add ObbState twice. "
2442 + "This indicates an error in the MountService logic.");
Kenny Root5919ac62010-10-05 09:49:40 -07002443 }
2444 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002445 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07002446
2447 obbStates.add(obbState);
2448 try {
2449 obbState.link();
2450 } catch (RemoteException e) {
2451 /*
2452 * The binder died before we could link it, so clean up our state
2453 * and return failure.
2454 */
2455 obbStates.remove(obbState);
2456 if (obbStates.isEmpty()) {
2457 mObbMounts.remove(binder);
2458 }
2459
2460 // Rethrow the error so mountObb can get it
2461 throw e;
2462 }
2463
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002464 mObbPathToStateMap.put(obbState.rawPath, obbState);
Kenny Roota02b8b02010-08-05 16:14:17 -07002465 }
2466
Kenny Rootaf9d6672010-10-08 09:21:39 -07002467 private void removeObbStateLocked(ObbState obbState) {
2468 final IBinder binder = obbState.getBinder();
2469 final List<ObbState> obbStates = mObbMounts.get(binder);
2470 if (obbStates != null) {
2471 if (obbStates.remove(obbState)) {
2472 obbState.unlink();
Kenny Root05105f72010-09-22 17:29:43 -07002473 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07002474 if (obbStates.isEmpty()) {
2475 mObbMounts.remove(binder);
2476 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002477 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002478
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002479 mObbPathToStateMap.remove(obbState.rawPath);
Kenny Root38cf8862010-09-26 14:18:51 -07002480 }
2481
Kenny Roota02b8b02010-08-05 16:14:17 -07002482 private class ObbActionHandler extends Handler {
2483 private boolean mBound = false;
Kenny Root480afe72010-10-07 10:17:50 -07002484 private final List<ObbAction> mActions = new LinkedList<ObbAction>();
Kenny Roota02b8b02010-08-05 16:14:17 -07002485
2486 ObbActionHandler(Looper l) {
2487 super(l);
2488 }
2489
2490 @Override
2491 public void handleMessage(Message msg) {
2492 switch (msg.what) {
2493 case OBB_RUN_ACTION: {
Kenny Root480afe72010-10-07 10:17:50 -07002494 final ObbAction action = (ObbAction) msg.obj;
Kenny Roota02b8b02010-08-05 16:14:17 -07002495
2496 if (DEBUG_OBB)
2497 Slog.i(TAG, "OBB_RUN_ACTION: " + action.toString());
2498
2499 // If a bind was already initiated we don't really
2500 // need to do anything. The pending install
2501 // will be processed later on.
2502 if (!mBound) {
2503 // If this is the only one pending we might
2504 // have to bind to the service again.
2505 if (!connectToService()) {
2506 Slog.e(TAG, "Failed to bind to media container service");
2507 action.handleError();
2508 return;
Kenny Roota02b8b02010-08-05 16:14:17 -07002509 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002510 }
Kenny Root735de3b2010-09-30 14:11:39 -07002511
Kenny Root735de3b2010-09-30 14:11:39 -07002512 mActions.add(action);
Kenny Roota02b8b02010-08-05 16:14:17 -07002513 break;
2514 }
2515 case OBB_MCS_BOUND: {
2516 if (DEBUG_OBB)
2517 Slog.i(TAG, "OBB_MCS_BOUND");
2518 if (msg.obj != null) {
2519 mContainerService = (IMediaContainerService) msg.obj;
2520 }
2521 if (mContainerService == null) {
2522 // Something seriously wrong. Bail out
2523 Slog.e(TAG, "Cannot bind to media container service");
2524 for (ObbAction action : mActions) {
2525 // Indicate service bind error
2526 action.handleError();
2527 }
2528 mActions.clear();
2529 } else if (mActions.size() > 0) {
Kenny Root480afe72010-10-07 10:17:50 -07002530 final ObbAction action = mActions.get(0);
Kenny Roota02b8b02010-08-05 16:14:17 -07002531 if (action != null) {
2532 action.execute(this);
2533 }
2534 } else {
2535 // Should never happen ideally.
2536 Slog.w(TAG, "Empty queue");
2537 }
2538 break;
2539 }
2540 case OBB_MCS_RECONNECT: {
2541 if (DEBUG_OBB)
2542 Slog.i(TAG, "OBB_MCS_RECONNECT");
2543 if (mActions.size() > 0) {
2544 if (mBound) {
2545 disconnectService();
2546 }
2547 if (!connectToService()) {
2548 Slog.e(TAG, "Failed to bind to media container service");
2549 for (ObbAction action : mActions) {
2550 // Indicate service bind error
2551 action.handleError();
2552 }
2553 mActions.clear();
2554 }
2555 }
2556 break;
2557 }
2558 case OBB_MCS_UNBIND: {
2559 if (DEBUG_OBB)
2560 Slog.i(TAG, "OBB_MCS_UNBIND");
2561
2562 // Delete pending install
2563 if (mActions.size() > 0) {
2564 mActions.remove(0);
2565 }
2566 if (mActions.size() == 0) {
2567 if (mBound) {
2568 disconnectService();
2569 }
2570 } else {
2571 // There are more pending requests in queue.
2572 // Just post MCS_BOUND message to trigger processing
2573 // of next pending install.
2574 mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND);
2575 }
2576 break;
2577 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07002578 case OBB_FLUSH_MOUNT_STATE: {
2579 final String path = (String) msg.obj;
2580
2581 if (DEBUG_OBB)
2582 Slog.i(TAG, "Flushing all OBB state for path " + path);
2583
2584 synchronized (mObbMounts) {
2585 final List<ObbState> obbStatesToRemove = new LinkedList<ObbState>();
2586
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002587 final Iterator<ObbState> i = mObbPathToStateMap.values().iterator();
Kenny Rootaf9d6672010-10-08 09:21:39 -07002588 while (i.hasNext()) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002589 final ObbState state = i.next();
Kenny Rootaf9d6672010-10-08 09:21:39 -07002590
2591 /*
2592 * If this entry's source file is in the volume path
2593 * that got unmounted, remove it because it's no
2594 * longer valid.
2595 */
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002596 if (state.canonicalPath.startsWith(path)) {
2597 obbStatesToRemove.add(state);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002598 }
2599 }
2600
2601 for (final ObbState obbState : obbStatesToRemove) {
2602 if (DEBUG_OBB)
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002603 Slog.i(TAG, "Removing state for " + obbState.rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002604
2605 removeObbStateLocked(obbState);
2606
2607 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002608 obbState.token.onObbResult(obbState.rawPath, obbState.nonce,
Kenny Rootaf9d6672010-10-08 09:21:39 -07002609 OnObbStateChangeListener.UNMOUNTED);
2610 } catch (RemoteException e) {
2611 Slog.i(TAG, "Couldn't send unmount notification for OBB: "
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002612 + obbState.rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002613 }
2614 }
2615 }
2616 break;
2617 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002618 }
2619 }
2620
2621 private boolean connectToService() {
2622 if (DEBUG_OBB)
2623 Slog.i(TAG, "Trying to bind to DefaultContainerService");
2624
2625 Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
2626 if (mContext.bindService(service, mDefContainerConn, Context.BIND_AUTO_CREATE)) {
2627 mBound = true;
2628 return true;
2629 }
2630 return false;
2631 }
2632
2633 private void disconnectService() {
2634 mContainerService = null;
2635 mBound = false;
2636 mContext.unbindService(mDefContainerConn);
2637 }
2638 }
2639
2640 abstract class ObbAction {
2641 private static final int MAX_RETRIES = 3;
2642 private int mRetries;
2643
2644 ObbState mObbState;
2645
2646 ObbAction(ObbState obbState) {
2647 mObbState = obbState;
2648 }
2649
2650 public void execute(ObbActionHandler handler) {
2651 try {
2652 if (DEBUG_OBB)
Ben Komalo444eca22011-09-01 15:17:44 -07002653 Slog.i(TAG, "Starting to execute action: " + toString());
Kenny Roota02b8b02010-08-05 16:14:17 -07002654 mRetries++;
2655 if (mRetries > MAX_RETRIES) {
2656 Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
Kenny Root480afe72010-10-07 10:17:50 -07002657 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
Kenny Roota02b8b02010-08-05 16:14:17 -07002658 handleError();
2659 return;
2660 } else {
2661 handleExecute();
2662 if (DEBUG_OBB)
2663 Slog.i(TAG, "Posting install MCS_UNBIND");
2664 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
2665 }
2666 } catch (RemoteException e) {
2667 if (DEBUG_OBB)
2668 Slog.i(TAG, "Posting install MCS_RECONNECT");
2669 mObbActionHandler.sendEmptyMessage(OBB_MCS_RECONNECT);
2670 } catch (Exception e) {
2671 if (DEBUG_OBB)
2672 Slog.d(TAG, "Error handling OBB action", e);
2673 handleError();
Kenny Root17eb6fb2010-10-06 15:02:52 -07002674 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
Kenny Roota02b8b02010-08-05 16:14:17 -07002675 }
2676 }
2677
Kenny Root05105f72010-09-22 17:29:43 -07002678 abstract void handleExecute() throws RemoteException, IOException;
Kenny Roota02b8b02010-08-05 16:14:17 -07002679 abstract void handleError();
Kenny Root38cf8862010-09-26 14:18:51 -07002680
2681 protected ObbInfo getObbInfo() throws IOException {
2682 ObbInfo obbInfo;
2683 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002684 obbInfo = mContainerService.getObbInfo(mObbState.ownerPath);
Kenny Root38cf8862010-09-26 14:18:51 -07002685 } catch (RemoteException e) {
2686 Slog.d(TAG, "Couldn't call DefaultContainerService to fetch OBB info for "
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002687 + mObbState.ownerPath);
Kenny Root38cf8862010-09-26 14:18:51 -07002688 obbInfo = null;
2689 }
2690 if (obbInfo == null) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002691 throw new IOException("Couldn't read OBB file: " + mObbState.ownerPath);
Kenny Root38cf8862010-09-26 14:18:51 -07002692 }
2693 return obbInfo;
2694 }
2695
Kenny Rootaf9d6672010-10-08 09:21:39 -07002696 protected void sendNewStatusOrIgnore(int status) {
2697 if (mObbState == null || mObbState.token == null) {
2698 return;
2699 }
2700
Kenny Root38cf8862010-09-26 14:18:51 -07002701 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002702 mObbState.token.onObbResult(mObbState.rawPath, mObbState.nonce, status);
Kenny Root38cf8862010-09-26 14:18:51 -07002703 } catch (RemoteException e) {
2704 Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
2705 }
2706 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002707 }
2708
2709 class MountObbAction extends ObbAction {
Ben Komalo444eca22011-09-01 15:17:44 -07002710 private final String mKey;
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002711 private final int mCallingUid;
Kenny Roota02b8b02010-08-05 16:14:17 -07002712
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002713 MountObbAction(ObbState obbState, String key, int callingUid) {
Kenny Roota02b8b02010-08-05 16:14:17 -07002714 super(obbState);
2715 mKey = key;
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002716 mCallingUid = callingUid;
Kenny Roota02b8b02010-08-05 16:14:17 -07002717 }
2718
Jason parks5af0b912010-11-29 09:05:25 -06002719 @Override
Kenny Root735de3b2010-09-30 14:11:39 -07002720 public void handleExecute() throws IOException, RemoteException {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002721 waitForReady();
2722 warnOnNotMounted();
2723
Kenny Root38cf8862010-09-26 14:18:51 -07002724 final ObbInfo obbInfo = getObbInfo();
2725
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002726 if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mCallingUid)) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002727 Slog.w(TAG, "Denied attempt to mount OBB " + obbInfo.filename
2728 + " which is owned by " + obbInfo.packageName);
2729 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
2730 return;
Kenny Roota02b8b02010-08-05 16:14:17 -07002731 }
2732
Kenny Rootaf9d6672010-10-08 09:21:39 -07002733 final boolean isMounted;
2734 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002735 isMounted = mObbPathToStateMap.containsKey(mObbState.rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002736 }
2737 if (isMounted) {
2738 Slog.w(TAG, "Attempt to mount OBB which is already mounted: " + obbInfo.filename);
2739 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_ALREADY_MOUNTED);
2740 return;
2741 }
2742
Kenny Rootaf9d6672010-10-08 09:21:39 -07002743 final String hashedKey;
2744 if (mKey == null) {
2745 hashedKey = "none";
2746 } else {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002747 try {
Kenny Root3b1abba2010-10-13 15:00:07 -07002748 SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
2749
2750 KeySpec ks = new PBEKeySpec(mKey.toCharArray(), obbInfo.salt,
2751 PBKDF2_HASH_ROUNDS, CRYPTO_ALGORITHM_KEY_SIZE);
2752 SecretKey key = factory.generateSecret(ks);
2753 BigInteger bi = new BigInteger(key.getEncoded());
2754 hashedKey = bi.toString(16);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002755 } catch (NoSuchAlgorithmException e) {
Kenny Root3b1abba2010-10-13 15:00:07 -07002756 Slog.e(TAG, "Could not load PBKDF2 algorithm", e);
2757 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
2758 return;
2759 } catch (InvalidKeySpecException e) {
2760 Slog.e(TAG, "Invalid key spec when loading PBKDF2 algorithm", e);
2761 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
Kenny Root38cf8862010-09-26 14:18:51 -07002762 return;
2763 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07002764 }
Kenny Root38cf8862010-09-26 14:18:51 -07002765
Kenny Rootaf9d6672010-10-08 09:21:39 -07002766 int rc = StorageResultCode.OperationSucceeded;
Kenny Rootaf9d6672010-10-08 09:21:39 -07002767 try {
Jeff Sharkey56cd6462013-06-07 15:09:15 -07002768 mConnector.execute("obb", "mount", mObbState.voldPath, new SensitiveArg(hashedKey),
2769 mObbState.ownerGid);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002770 } catch (NativeDaemonConnectorException e) {
2771 int code = e.getCode();
2772 if (code != VoldResponseCode.OpFailedStorageBusy) {
2773 rc = StorageResultCode.OperationFailedInternalError;
Kenny Roota02b8b02010-08-05 16:14:17 -07002774 }
2775 }
2776
Kenny Rootaf9d6672010-10-08 09:21:39 -07002777 if (rc == StorageResultCode.OperationSucceeded) {
2778 if (DEBUG_OBB)
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002779 Slog.d(TAG, "Successfully mounted OBB " + mObbState.voldPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002780
2781 synchronized (mObbMounts) {
2782 addObbStateLocked(mObbState);
2783 }
2784
2785 sendNewStatusOrIgnore(OnObbStateChangeListener.MOUNTED);
Kenny Root02c87302010-07-01 08:10:18 -07002786 } else {
Kenny Root05105f72010-09-22 17:29:43 -07002787 Slog.e(TAG, "Couldn't mount OBB file: " + rc);
Kenny Roota02b8b02010-08-05 16:14:17 -07002788
Kenny Rootaf9d6672010-10-08 09:21:39 -07002789 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT);
Kenny Root02c87302010-07-01 08:10:18 -07002790 }
2791 }
2792
Jason parks5af0b912010-11-29 09:05:25 -06002793 @Override
Kenny Roota02b8b02010-08-05 16:14:17 -07002794 public void handleError() {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002795 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
Kenny Root02c87302010-07-01 08:10:18 -07002796 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002797
2798 @Override
2799 public String toString() {
2800 StringBuilder sb = new StringBuilder();
2801 sb.append("MountObbAction{");
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002802 sb.append(mObbState);
Kenny Roota02b8b02010-08-05 16:14:17 -07002803 sb.append('}');
2804 return sb.toString();
2805 }
2806 }
2807
2808 class UnmountObbAction extends ObbAction {
Ben Komalo444eca22011-09-01 15:17:44 -07002809 private final boolean mForceUnmount;
Kenny Roota02b8b02010-08-05 16:14:17 -07002810
2811 UnmountObbAction(ObbState obbState, boolean force) {
2812 super(obbState);
2813 mForceUnmount = force;
2814 }
2815
Jason parks5af0b912010-11-29 09:05:25 -06002816 @Override
Kenny Root38cf8862010-09-26 14:18:51 -07002817 public void handleExecute() throws IOException {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002818 waitForReady();
2819 warnOnNotMounted();
2820
Kenny Root38cf8862010-09-26 14:18:51 -07002821 final ObbInfo obbInfo = getObbInfo();
Kenny Roota02b8b02010-08-05 16:14:17 -07002822
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002823 final ObbState existingState;
Kenny Root38cf8862010-09-26 14:18:51 -07002824 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002825 existingState = mObbPathToStateMap.get(mObbState.rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002826 }
Kenny Root38cf8862010-09-26 14:18:51 -07002827
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002828 if (existingState == null) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002829 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_NOT_MOUNTED);
2830 return;
2831 }
2832
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002833 if (existingState.ownerGid != mObbState.ownerGid) {
2834 Slog.w(TAG, "Permission denied attempting to unmount OBB " + existingState.rawPath
2835 + " (owned by GID " + existingState.ownerGid + ")");
Kenny Rootaf9d6672010-10-08 09:21:39 -07002836 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
2837 return;
2838 }
2839
Kenny Rootaf9d6672010-10-08 09:21:39 -07002840 int rc = StorageResultCode.OperationSucceeded;
Kenny Rootaf9d6672010-10-08 09:21:39 -07002841 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002842 final Command cmd = new Command("obb", "unmount", mObbState.voldPath);
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002843 if (mForceUnmount) {
2844 cmd.appendArg("force");
2845 }
2846 mConnector.execute(cmd);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002847 } catch (NativeDaemonConnectorException e) {
2848 int code = e.getCode();
2849 if (code == VoldResponseCode.OpFailedStorageBusy) {
2850 rc = StorageResultCode.OperationFailedStorageBusy;
2851 } else if (code == VoldResponseCode.OpFailedStorageNotFound) {
2852 // If it's not mounted then we've already won.
2853 rc = StorageResultCode.OperationSucceeded;
2854 } else {
2855 rc = StorageResultCode.OperationFailedInternalError;
Kenny Roota02b8b02010-08-05 16:14:17 -07002856 }
2857 }
2858
Kenny Rootaf9d6672010-10-08 09:21:39 -07002859 if (rc == StorageResultCode.OperationSucceeded) {
2860 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002861 removeObbStateLocked(existingState);
Kenny Root38cf8862010-09-26 14:18:51 -07002862 }
2863
Kenny Rootaf9d6672010-10-08 09:21:39 -07002864 sendNewStatusOrIgnore(OnObbStateChangeListener.UNMOUNTED);
Kenny Roota02b8b02010-08-05 16:14:17 -07002865 } else {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002866 Slog.w(TAG, "Could not unmount OBB: " + existingState);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002867 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_UNMOUNT);
Kenny Roota02b8b02010-08-05 16:14:17 -07002868 }
2869 }
2870
Jason parks5af0b912010-11-29 09:05:25 -06002871 @Override
Kenny Roota02b8b02010-08-05 16:14:17 -07002872 public void handleError() {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002873 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
Kenny Roota02b8b02010-08-05 16:14:17 -07002874 }
2875
2876 @Override
2877 public String toString() {
2878 StringBuilder sb = new StringBuilder();
2879 sb.append("UnmountObbAction{");
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002880 sb.append(mObbState);
Kenny Roota02b8b02010-08-05 16:14:17 -07002881 sb.append(",force=");
2882 sb.append(mForceUnmount);
Kenny Roota02b8b02010-08-05 16:14:17 -07002883 sb.append('}');
2884 return sb.toString();
2885 }
Kenny Root02c87302010-07-01 08:10:18 -07002886 }
Kenny Root38cf8862010-09-26 14:18:51 -07002887
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -08002888 @VisibleForTesting
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002889 public static String buildObbPath(final String canonicalPath, int userId, boolean forVold) {
2890 // TODO: allow caller to provide Environment for full testing
Jeff Sharkey1abdb712013-08-11 16:28:14 -07002891 // TODO: extend to support OBB mounts on secondary external storage
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002892
2893 // Only adjust paths when storage is emulated
2894 if (!Environment.isExternalStorageEmulated()) {
2895 return canonicalPath;
2896 }
2897
2898 String path = canonicalPath.toString();
2899
2900 // First trim off any external storage prefix
2901 final UserEnvironment userEnv = new UserEnvironment(userId);
2902
2903 // /storage/emulated/0
Jeff Sharkey1abdb712013-08-11 16:28:14 -07002904 final String externalPath = userEnv.getExternalStorageDirectory().getAbsolutePath();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002905 // /storage/emulated_legacy
2906 final String legacyExternalPath = Environment.getLegacyExternalStorageDirectory()
Jeff Sharkey1abdb712013-08-11 16:28:14 -07002907 .getAbsolutePath();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002908
2909 if (path.startsWith(externalPath)) {
2910 path = path.substring(externalPath.length() + 1);
2911 } else if (path.startsWith(legacyExternalPath)) {
2912 path = path.substring(legacyExternalPath.length() + 1);
2913 } else {
2914 return canonicalPath;
2915 }
2916
2917 // Handle special OBB paths on emulated storage
2918 final String obbPath = "Android/obb";
2919 if (path.startsWith(obbPath)) {
2920 path = path.substring(obbPath.length() + 1);
2921
2922 if (forVold) {
Jeff Sharkey1abdb712013-08-11 16:28:14 -07002923 return new File(Environment.getEmulatedStorageObbSource(), path).getAbsolutePath();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002924 } else {
2925 final UserEnvironment ownerEnv = new UserEnvironment(UserHandle.USER_OWNER);
Jeff Sharkey1abdb712013-08-11 16:28:14 -07002926 return new File(ownerEnv.buildExternalStorageAndroidObbDirs()[0], path)
2927 .getAbsolutePath();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002928 }
2929 }
2930
2931 // Handle normal external storage paths
2932 if (forVold) {
Jeff Sharkey1abdb712013-08-11 16:28:14 -07002933 return new File(Environment.getEmulatedStorageSource(userId), path).getAbsolutePath();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002934 } else {
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002935 return new File(userEnv.getExternalDirsForApp()[0], path).getAbsolutePath();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002936 }
2937 }
2938
Kenny Root38cf8862010-09-26 14:18:51 -07002939 @Override
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002940 protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
2941 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
2942
2943 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ", 160);
Kenny Root38cf8862010-09-26 14:18:51 -07002944
Kenny Root38cf8862010-09-26 14:18:51 -07002945 synchronized (mObbMounts) {
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002946 pw.println("mObbMounts:");
2947 pw.increaseIndent();
2948 final Iterator<Entry<IBinder, List<ObbState>>> binders = mObbMounts.entrySet()
2949 .iterator();
Kenny Rootaf9d6672010-10-08 09:21:39 -07002950 while (binders.hasNext()) {
2951 Entry<IBinder, List<ObbState>> e = binders.next();
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002952 pw.println(e.getKey() + ":");
2953 pw.increaseIndent();
Kenny Rootaf9d6672010-10-08 09:21:39 -07002954 final List<ObbState> obbStates = e.getValue();
Kenny Root38cf8862010-09-26 14:18:51 -07002955 for (final ObbState obbState : obbStates) {
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002956 pw.println(obbState);
Kenny Root38cf8862010-09-26 14:18:51 -07002957 }
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002958 pw.decreaseIndent();
Kenny Root38cf8862010-09-26 14:18:51 -07002959 }
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002960 pw.decreaseIndent();
Kenny Rootaf9d6672010-10-08 09:21:39 -07002961
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002962 pw.println();
2963 pw.println("mObbPathToStateMap:");
2964 pw.increaseIndent();
Kenny Rootaf9d6672010-10-08 09:21:39 -07002965 final Iterator<Entry<String, ObbState>> maps = mObbPathToStateMap.entrySet().iterator();
2966 while (maps.hasNext()) {
2967 final Entry<String, ObbState> e = maps.next();
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002968 pw.print(e.getKey());
2969 pw.print(" -> ");
2970 pw.println(e.getValue());
Kenny Rootaf9d6672010-10-08 09:21:39 -07002971 }
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002972 pw.decreaseIndent();
Kenny Root38cf8862010-09-26 14:18:51 -07002973 }
Kenny Root4161f9b2011-07-13 09:48:33 -07002974
Jeff Sharkeyb049e212012-09-07 23:16:01 -07002975 synchronized (mVolumesLock) {
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002976 pw.println();
2977 pw.println("mVolumes:");
2978 pw.increaseIndent();
2979 for (StorageVolume volume : mVolumes) {
2980 pw.println(volume);
2981 pw.increaseIndent();
2982 pw.println("Current state: " + mVolumeStates.get(volume.getPath()));
2983 pw.decreaseIndent();
Kenny Root4161f9b2011-07-13 09:48:33 -07002984 }
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002985 pw.decreaseIndent();
Kenny Root4161f9b2011-07-13 09:48:33 -07002986 }
Robert Greenwalt470fd722012-01-18 12:51:15 -08002987
2988 pw.println();
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002989 pw.println("mConnection:");
2990 pw.increaseIndent();
Robert Greenwalt470fd722012-01-18 12:51:15 -08002991 mConnector.dump(fd, pw, args);
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002992 pw.decreaseIndent();
Kenny Root38cf8862010-09-26 14:18:51 -07002993 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002994
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -07002995 /** {@inheritDoc} */
2996 public void monitor() {
2997 if (mConnector != null) {
2998 mConnector.monitor();
2999 }
3000 }
3001}