blob: ea24d7c9543c8183730a6f5647afe9b95d8ddf96 [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;
Emily Bernier92aa5a22014-07-07 10:11:48 -040050import android.os.UserManager;
Kenny Roota02b8b02010-08-05 16:14:17 -070051import android.os.storage.IMountService;
52import android.os.storage.IMountServiceListener;
53import android.os.storage.IMountShutdownObserver;
54import android.os.storage.IObbActionListener;
Kenny Rootaf9d6672010-10-08 09:21:39 -070055import android.os.storage.OnObbStateChangeListener;
Paul Lawrence46791e72014-04-03 09:10:26 -070056import android.os.storage.StorageManager;
Kenny Roota02b8b02010-08-05 16:14:17 -070057import android.os.storage.StorageResultCode;
Mike Lockwood2f6a3882011-05-09 19:08:06 -070058import android.os.storage.StorageVolume;
Jason parksf7b3cd42011-01-27 09:28:25 -060059import android.text.TextUtils;
Mike Lockwood2f6a3882011-05-09 19:08:06 -070060import android.util.AttributeSet;
San Mehata5078592010-03-25 09:36:54 -070061import android.util.Slog;
Mike Lockwood2f6a3882011-05-09 19:08:06 -070062import android.util.Xml;
63
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -080064import com.android.internal.annotations.GuardedBy;
65import com.android.internal.annotations.VisibleForTesting;
Jeff Sharkeyb049e212012-09-07 23:16:01 -070066import com.android.internal.app.IMediaContainerService;
Jeff Sharkey5aca2b82013-10-16 16:21:54 -070067import com.android.internal.util.IndentingPrintWriter;
Jeff Sharkey4fbbda42012-09-24 18:34:07 -070068import com.android.internal.util.Preconditions;
Jeff Sharkeyb049e212012-09-07 23:16:01 -070069import com.android.internal.util.XmlUtils;
70import com.android.server.NativeDaemonConnector.Command;
Jeff Sharkey56cd6462013-06-07 15:09:15 -070071import com.android.server.NativeDaemonConnector.SensitiveArg;
Jeff Sharkeyb049e212012-09-07 23:16:01 -070072import com.android.server.am.ActivityManagerService;
73import com.android.server.pm.PackageManagerService;
74import com.android.server.pm.UserManagerService;
75import com.google.android.collect.Lists;
76import com.google.android.collect.Maps;
77
Paul Lawrence8e397362014-01-27 15:22:30 -080078import org.apache.commons.codec.binary.Hex;
Paul Lawrence945490c2014-03-27 16:37:28 +000079import org.apache.commons.codec.DecoderException;
Mike Lockwood2f6a3882011-05-09 19:08:06 -070080import org.xmlpull.v1.XmlPullParserException;
Kenny Roota02b8b02010-08-05 16:14:17 -070081
Jeff Sharkeyb049e212012-09-07 23:16:01 -070082import java.io.File;
Kenny Root38cf8862010-09-26 14:18:51 -070083import java.io.FileDescriptor;
Kenny Root05105f72010-09-22 17:29:43 -070084import java.io.IOException;
Kenny Root38cf8862010-09-26 14:18:51 -070085import java.io.PrintWriter;
Kenny Root3b1abba2010-10-13 15:00:07 -070086import java.math.BigInteger;
Paul Lawrence8e397362014-01-27 15:22:30 -080087import java.nio.charset.StandardCharsets;
Kenny Root735de3b2010-09-30 14:11:39 -070088import java.security.NoSuchAlgorithmException;
Kenny Root3b1abba2010-10-13 15:00:07 -070089import java.security.spec.InvalidKeySpecException;
90import java.security.spec.KeySpec;
San Mehat22dd86e2010-01-12 12:21:18 -080091import java.util.ArrayList;
Kenny Roota02b8b02010-08-05 16:14:17 -070092import java.util.HashMap;
San Mehat6cdd9c02010-02-09 14:45:20 -080093import java.util.HashSet;
Kenny Root38cf8862010-09-26 14:18:51 -070094import java.util.Iterator;
Kenny Roota02b8b02010-08-05 16:14:17 -070095import java.util.LinkedList;
96import java.util.List;
97import java.util.Map;
Kenny Root38cf8862010-09-26 14:18:51 -070098import java.util.Map.Entry;
Rickard Helldin5fb5df02014-02-07 09:35:04 +010099import java.util.concurrent.atomic.AtomicInteger;
Kenny Root51a573c2012-05-17 13:30:28 -0700100import java.util.concurrent.CountDownLatch;
101import java.util.concurrent.TimeUnit;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102
Kenny Root3b1abba2010-10-13 15:00:07 -0700103import javax.crypto.SecretKey;
104import javax.crypto.SecretKeyFactory;
105import javax.crypto.spec.PBEKeySpec;
106
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800107/**
San Mehatb1043402010-02-05 08:26:50 -0800108 * MountService implements back-end services for platform storage
109 * management.
110 * @hide - Applications should use android.os.storage.StorageManager
111 * to access the MountService.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112 */
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -0700113class MountService extends IMountService.Stub
114 implements INativeDaemonConnectorCallbacks, Watchdog.Monitor {
Jason parks5af0b912010-11-29 09:05:25 -0600115
Christopher Tated417d622013-08-19 16:14:25 -0700116 // Static direct instance pointer for the tightly-coupled idle service to use
117 static MountService sSelf = null;
118
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700119 // TODO: listen for user creation/deletion
120
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800121 private static final boolean LOCAL_LOGD = false;
122 private static final boolean DEBUG_UNMOUNT = false;
123 private static final boolean DEBUG_EVENTS = false;
Kenny Rootb7db2722011-01-25 16:39:35 -0800124 private static final boolean DEBUG_OBB = false;
Kenny Root02c87302010-07-01 08:10:18 -0700125
Kenny Root07714d42011-08-17 17:49:28 -0700126 // Disable this since it messes up long-running cryptfs operations.
127 private static final boolean WATCHDOG_ENABLE = false;
128
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800129 private static final String TAG = "MountService";
130
Kenny Root305bcbf2010-09-03 07:56:38 -0700131 private static final String VOLD_TAG = "VoldConnector";
132
Kenny Rootcf0b38c2011-03-22 14:17:59 -0700133 /** Maximum number of ASEC containers allowed to be mounted. */
134 private static final int MAX_CONTAINERS = 250;
135
San Mehat4270e1e2010-01-29 05:32:19 -0800136 /*
137 * Internal vold volume state constants
138 */
San Mehat7fd0fee2009-12-17 07:12:23 -0800139 class VolumeState {
140 public static final int Init = -1;
141 public static final int NoMedia = 0;
142 public static final int Idle = 1;
143 public static final int Pending = 2;
144 public static final int Checking = 3;
145 public static final int Mounted = 4;
146 public static final int Unmounting = 5;
147 public static final int Formatting = 6;
148 public static final int Shared = 7;
149 public static final int SharedMnt = 8;
150 }
151
San Mehat4270e1e2010-01-29 05:32:19 -0800152 /*
153 * Internal vold response code constants
154 */
San Mehat22dd86e2010-01-12 12:21:18 -0800155 class VoldResponseCode {
San Mehat4270e1e2010-01-29 05:32:19 -0800156 /*
157 * 100 series - Requestion action was initiated; expect another reply
158 * before proceeding with a new command.
159 */
San Mehat22dd86e2010-01-12 12:21:18 -0800160 public static final int VolumeListResult = 110;
161 public static final int AsecListResult = 111;
San Mehatc1b4ce92010-02-16 17:13:03 -0800162 public static final int StorageUsersListResult = 112;
Paul Lawrencee51dcf92014-03-18 10:56:00 -0700163 public static final int CryptfsGetfieldResult = 113;
San Mehat22dd86e2010-01-12 12:21:18 -0800164
San Mehat4270e1e2010-01-29 05:32:19 -0800165 /*
166 * 200 series - Requestion action has been successfully completed.
167 */
168 public static final int ShareStatusResult = 210;
San Mehat22dd86e2010-01-12 12:21:18 -0800169 public static final int AsecPathResult = 211;
San Mehat4270e1e2010-01-29 05:32:19 -0800170 public static final int ShareEnabledResult = 212;
San Mehat22dd86e2010-01-12 12:21:18 -0800171
San Mehat4270e1e2010-01-29 05:32:19 -0800172 /*
173 * 400 series - Command was accepted, but the requested action
174 * did not take place.
175 */
176 public static final int OpFailedNoMedia = 401;
177 public static final int OpFailedMediaBlank = 402;
178 public static final int OpFailedMediaCorrupt = 403;
179 public static final int OpFailedVolNotMounted = 404;
San Mehatd9709982010-02-18 11:43:03 -0800180 public static final int OpFailedStorageBusy = 405;
San Mehat2d66cef2010-03-23 11:12:52 -0700181 public static final int OpFailedStorageNotFound = 406;
San Mehat4270e1e2010-01-29 05:32:19 -0800182
183 /*
184 * 600 series - Unsolicited broadcasts.
185 */
San Mehat22dd86e2010-01-12 12:21:18 -0800186 public static final int VolumeStateChange = 605;
Jeff Sharkey5aca2b82013-10-16 16:21:54 -0700187 public static final int VolumeUuidChange = 613;
188 public static final int VolumeUserLabelChange = 614;
San Mehat22dd86e2010-01-12 12:21:18 -0800189 public static final int VolumeDiskInserted = 630;
190 public static final int VolumeDiskRemoved = 631;
191 public static final int VolumeBadRemoval = 632;
Svetoslavf23b64d2013-04-25 14:45:54 -0700192
193 /*
194 * 700 series - fstrim
195 */
196 public static final int FstrimCompleted = 700;
San Mehat22dd86e2010-01-12 12:21:18 -0800197 }
198
Paul Lawrence8e397362014-01-27 15:22:30 -0800199 /** List of crypto types.
200 * These must match CRYPT_TYPE_XXX in cryptfs.h AND their
201 * corresponding commands in CommandListener.cpp */
202 public static final String[] CRYPTO_TYPES
203 = { "password", "default", "pattern", "pin" };
204
Brian Carlstrom7395a8a2014-04-28 22:11:01 -0700205 private final Context mContext;
Brian Carlstromdfad99a2014-05-07 15:21:14 -0700206 private final NativeDaemonConnector mConnector;
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700207
208 private final Object mVolumesLock = new Object();
209
210 /** When defined, base template for user-specific {@link StorageVolume}. */
211 private StorageVolume mEmulatedTemplate;
212
Jeff Sharkey1abdb712013-08-11 16:28:14 -0700213 // TODO: separate storage volumes on per-user basis
214
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -0800215 @GuardedBy("mVolumesLock")
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700216 private final ArrayList<StorageVolume> mVolumes = Lists.newArrayList();
217 /** Map from path to {@link StorageVolume} */
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -0800218 @GuardedBy("mVolumesLock")
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700219 private final HashMap<String, StorageVolume> mVolumesByPath = Maps.newHashMap();
220 /** Map from path to state */
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -0800221 @GuardedBy("mVolumesLock")
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700222 private final HashMap<String, String> mVolumeStates = Maps.newHashMap();
223
224 private volatile boolean mSystemReady = false;
225
San Mehat4270e1e2010-01-29 05:32:19 -0800226 private PackageManagerService mPms;
227 private boolean mUmsEnabling;
Mike Lockwoodecedfdc2011-06-08 15:11:59 -0700228 private boolean mUmsAvailable = false;
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800229 // Used as a lock for methods that register/unregister listeners.
230 final private ArrayList<MountServiceBinderListener> mListeners =
231 new ArrayList<MountServiceBinderListener>();
Jeff Sharkey0be607c2012-11-14 14:39:19 -0800232 private final CountDownLatch mConnectedSignal = new CountDownLatch(1);
233 private final CountDownLatch mAsecsScanned = new CountDownLatch(1);
San Mehat6a965af22010-02-24 17:47:30 -0800234 private boolean mSendUmsConnectedOnBoot = false;
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -0800235
San Mehat6cdd9c02010-02-09 14:45:20 -0800236 /**
237 * Private hash of currently mounted secure containers.
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800238 * Used as a lock in methods to manipulate secure containers.
San Mehat6cdd9c02010-02-09 14:45:20 -0800239 */
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800240 final private HashSet<String> mAsecMountSet = new HashSet<String>();
San Mehat6cdd9c02010-02-09 14:45:20 -0800241
Kenny Root02c87302010-07-01 08:10:18 -0700242 /**
Kenny Root3b1abba2010-10-13 15:00:07 -0700243 * The size of the crypto algorithm key in bits for OBB files. Currently
244 * Twofish is used which takes 128-bit keys.
245 */
246 private static final int CRYPTO_ALGORITHM_KEY_SIZE = 128;
247
248 /**
249 * The number of times to run SHA1 in the PBKDF2 function for OBB files.
250 * 1024 is reasonably secure and not too slow.
251 */
252 private static final int PBKDF2_HASH_ROUNDS = 1024;
253
254 /**
Kenny Roota02b8b02010-08-05 16:14:17 -0700255 * Mounted OBB tracking information. Used to track the current state of all
256 * OBBs.
Kenny Root02c87302010-07-01 08:10:18 -0700257 */
Kenny Root735de3b2010-09-30 14:11:39 -0700258 final private Map<IBinder, List<ObbState>> mObbMounts = new HashMap<IBinder, List<ObbState>>();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700259
260 /** Map from raw paths to {@link ObbState}. */
Kenny Roota02b8b02010-08-05 16:14:17 -0700261 final private Map<String, ObbState> mObbPathToStateMap = new HashMap<String, ObbState>();
262
263 class ObbState implements IBinder.DeathRecipient {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700264 public ObbState(String rawPath, String canonicalPath, int callingUid,
265 IObbActionListener token, int nonce) {
266 this.rawPath = rawPath;
267 this.canonicalPath = canonicalPath.toString();
268
269 final int userId = UserHandle.getUserId(callingUid);
270 this.ownerPath = buildObbPath(canonicalPath, userId, false);
271 this.voldPath = buildObbPath(canonicalPath, userId, true);
272
273 this.ownerGid = UserHandle.getSharedAppGid(callingUid);
Kenny Rootaf9d6672010-10-08 09:21:39 -0700274 this.token = token;
275 this.nonce = nonce;
Kenny Roota02b8b02010-08-05 16:14:17 -0700276 }
277
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700278 final String rawPath;
279 final String canonicalPath;
280 final String ownerPath;
281 final String voldPath;
Kenny Roota02b8b02010-08-05 16:14:17 -0700282
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700283 final int ownerGid;
Kenny Roota02b8b02010-08-05 16:14:17 -0700284
Kenny Rootaf9d6672010-10-08 09:21:39 -0700285 // Token of remote Binder caller
286 final IObbActionListener token;
287
288 // Identifier to pass back to the token
289 final int nonce;
Kenny Roota02b8b02010-08-05 16:14:17 -0700290
Kenny Root735de3b2010-09-30 14:11:39 -0700291 public IBinder getBinder() {
292 return token.asBinder();
293 }
294
Kenny Roota02b8b02010-08-05 16:14:17 -0700295 @Override
296 public void binderDied() {
297 ObbAction action = new UnmountObbAction(this, true);
298 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
Kenny Root735de3b2010-09-30 14:11:39 -0700299 }
Kenny Roota02b8b02010-08-05 16:14:17 -0700300
Kenny Root5919ac62010-10-05 09:49:40 -0700301 public void link() throws RemoteException {
302 getBinder().linkToDeath(this, 0);
303 }
304
305 public void unlink() {
Kenny Root735de3b2010-09-30 14:11:39 -0700306 getBinder().unlinkToDeath(this, 0);
Kenny Roota02b8b02010-08-05 16:14:17 -0700307 }
Kenny Root38cf8862010-09-26 14:18:51 -0700308
309 @Override
310 public String toString() {
311 StringBuilder sb = new StringBuilder("ObbState{");
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700312 sb.append("rawPath=").append(rawPath);
313 sb.append(",canonicalPath=").append(canonicalPath);
314 sb.append(",ownerPath=").append(ownerPath);
315 sb.append(",voldPath=").append(voldPath);
316 sb.append(",ownerGid=").append(ownerGid);
317 sb.append(",token=").append(token);
318 sb.append(",binder=").append(getBinder());
Kenny Root38cf8862010-09-26 14:18:51 -0700319 sb.append('}');
320 return sb.toString();
321 }
Kenny Roota02b8b02010-08-05 16:14:17 -0700322 }
323
324 // OBB Action Handler
325 final private ObbActionHandler mObbActionHandler;
326
327 // OBB action handler messages
328 private static final int OBB_RUN_ACTION = 1;
329 private static final int OBB_MCS_BOUND = 2;
330 private static final int OBB_MCS_UNBIND = 3;
331 private static final int OBB_MCS_RECONNECT = 4;
Kenny Rootaf9d6672010-10-08 09:21:39 -0700332 private static final int OBB_FLUSH_MOUNT_STATE = 5;
Kenny Roota02b8b02010-08-05 16:14:17 -0700333
334 /*
335 * Default Container Service information
336 */
337 static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
338 "com.android.defcontainer", "com.android.defcontainer.DefaultContainerService");
339
340 final private DefaultContainerConnection mDefContainerConn = new DefaultContainerConnection();
341
342 class DefaultContainerConnection implements ServiceConnection {
343 public void onServiceConnected(ComponentName name, IBinder service) {
344 if (DEBUG_OBB)
345 Slog.i(TAG, "onServiceConnected");
346 IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(service);
347 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_MCS_BOUND, imcs));
348 }
349
350 public void onServiceDisconnected(ComponentName name) {
351 if (DEBUG_OBB)
352 Slog.i(TAG, "onServiceDisconnected");
353 }
354 };
355
356 // Used in the ObbActionHandler
357 private IMediaContainerService mContainerService = null;
Kenny Root02c87302010-07-01 08:10:18 -0700358
359 // Handler messages
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800360 private static final int H_UNMOUNT_PM_UPDATE = 1;
361 private static final int H_UNMOUNT_PM_DONE = 2;
362 private static final int H_UNMOUNT_MS = 3;
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700363 private static final int H_SYSTEM_READY = 4;
Christopher Tated417d622013-08-19 16:14:25 -0700364 private static final int H_FSTRIM = 5;
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700365
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800366 private static final int RETRY_UNMOUNT_DELAY = 30; // in ms
367 private static final int MAX_UNMOUNT_RETRIES = 4;
368
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800369 class UnmountCallBack {
Kenny Root05105f72010-09-22 17:29:43 -0700370 final String path;
371 final boolean force;
Ben Komalo13c71972011-09-07 16:35:56 -0700372 final boolean removeEncryption;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800373 int retries;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800374
Ben Komalo13c71972011-09-07 16:35:56 -0700375 UnmountCallBack(String path, boolean force, boolean removeEncryption) {
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800376 retries = 0;
377 this.path = path;
378 this.force = force;
Ben Komalo13c71972011-09-07 16:35:56 -0700379 this.removeEncryption = removeEncryption;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800380 }
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800381
382 void handleFinished() {
San Mehata5078592010-03-25 09:36:54 -0700383 if (DEBUG_UNMOUNT) Slog.i(TAG, "Unmounting " + path);
Ben Komalo13c71972011-09-07 16:35:56 -0700384 doUnmountVolume(path, true, removeEncryption);
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800385 }
386 }
387
388 class UmsEnableCallBack extends UnmountCallBack {
Kenny Root05105f72010-09-22 17:29:43 -0700389 final String method;
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800390
391 UmsEnableCallBack(String path, String method, boolean force) {
Ben Komalo13c71972011-09-07 16:35:56 -0700392 super(path, force, false);
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800393 this.method = method;
394 }
395
396 @Override
397 void handleFinished() {
398 super.handleFinished();
399 doShareUnshareVolume(path, method, true);
400 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800401 }
402
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800403 class ShutdownCallBack extends UnmountCallBack {
Rickard Helldin5fb5df02014-02-07 09:35:04 +0100404 MountShutdownLatch mMountShutdownLatch;
405 ShutdownCallBack(String path, final MountShutdownLatch mountShutdownLatch) {
Ben Komalo13c71972011-09-07 16:35:56 -0700406 super(path, true, false);
Rickard Helldin5fb5df02014-02-07 09:35:04 +0100407 mMountShutdownLatch = mountShutdownLatch;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800408 }
409
410 @Override
411 void handleFinished() {
Ben Komalo13c71972011-09-07 16:35:56 -0700412 int ret = doUnmountVolume(path, true, removeEncryption);
Rickard Helldin5fb5df02014-02-07 09:35:04 +0100413 Slog.i(TAG, "Unmount completed: " + path + ", result code: " + ret);
414 mMountShutdownLatch.countDown();
415 }
416 }
417
418 static class MountShutdownLatch {
419 private IMountShutdownObserver mObserver;
420 private AtomicInteger mCount;
421
422 MountShutdownLatch(final IMountShutdownObserver observer, int count) {
423 mObserver = observer;
424 mCount = new AtomicInteger(count);
425 }
426
427 void countDown() {
428 boolean sendShutdown = false;
429 if (mCount.decrementAndGet() == 0) {
430 sendShutdown = true;
431 }
432 if (sendShutdown && mObserver != null) {
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800433 try {
Rickard Helldin5fb5df02014-02-07 09:35:04 +0100434 mObserver.onShutDownComplete(StorageResultCode.OperationSucceeded);
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800435 } catch (RemoteException e) {
San Mehata5078592010-03-25 09:36:54 -0700436 Slog.w(TAG, "RemoteException when shutting down");
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800437 }
438 }
439 }
440 }
441
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400442 class MountServiceHandler extends Handler {
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800443 ArrayList<UnmountCallBack> mForceUnmounts = new ArrayList<UnmountCallBack>();
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700444 boolean mUpdatingStatus = false;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800445
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400446 MountServiceHandler(Looper l) {
447 super(l);
448 }
449
Jason parks5af0b912010-11-29 09:05:25 -0600450 @Override
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800451 public void handleMessage(Message msg) {
452 switch (msg.what) {
453 case H_UNMOUNT_PM_UPDATE: {
San Mehata5078592010-03-25 09:36:54 -0700454 if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_UPDATE");
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800455 UnmountCallBack ucb = (UnmountCallBack) msg.obj;
456 mForceUnmounts.add(ucb);
San Mehata5078592010-03-25 09:36:54 -0700457 if (DEBUG_UNMOUNT) Slog.i(TAG, " registered = " + mUpdatingStatus);
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800458 // Register only if needed.
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700459 if (!mUpdatingStatus) {
San Mehata5078592010-03-25 09:36:54 -0700460 if (DEBUG_UNMOUNT) Slog.i(TAG, "Updating external media status on PackageManager");
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700461 mUpdatingStatus = true;
462 mPms.updateExternalMediaStatus(false, true);
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800463 }
464 break;
465 }
466 case H_UNMOUNT_PM_DONE: {
San Mehata5078592010-03-25 09:36:54 -0700467 if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_DONE");
San Mehata5078592010-03-25 09:36:54 -0700468 if (DEBUG_UNMOUNT) Slog.i(TAG, "Updated status. Processing requests");
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700469 mUpdatingStatus = false;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800470 int size = mForceUnmounts.size();
471 int sizeArr[] = new int[size];
472 int sizeArrN = 0;
Suchi Amalapurapu7af074a2010-04-05 16:46:32 -0700473 // Kill processes holding references first
474 ActivityManagerService ams = (ActivityManagerService)
475 ServiceManager.getService("activity");
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800476 for (int i = 0; i < size; i++) {
477 UnmountCallBack ucb = mForceUnmounts.get(i);
478 String path = ucb.path;
479 boolean done = false;
480 if (!ucb.force) {
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800481 done = true;
482 } else {
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800483 int pids[] = getStorageUsers(path);
484 if (pids == null || pids.length == 0) {
485 done = true;
486 } else {
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800487 // Eliminate system process here?
Dianne Hackborn64825172011-03-02 21:32:58 -0800488 ams.killPids(pids, "unmount media", true);
Suchi Amalapurapu7af074a2010-04-05 16:46:32 -0700489 // Confirm if file references have been freed.
490 pids = getStorageUsers(path);
491 if (pids == null || pids.length == 0) {
492 done = true;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800493 }
494 }
495 }
Suchi Amalapurapu7af074a2010-04-05 16:46:32 -0700496 if (!done && (ucb.retries < MAX_UNMOUNT_RETRIES)) {
497 // Retry again
498 Slog.i(TAG, "Retrying to kill storage users again");
499 mHandler.sendMessageDelayed(
500 mHandler.obtainMessage(H_UNMOUNT_PM_DONE,
501 ucb.retries++),
502 RETRY_UNMOUNT_DELAY);
503 } else {
504 if (ucb.retries >= MAX_UNMOUNT_RETRIES) {
505 Slog.i(TAG, "Failed to unmount media inspite of " +
506 MAX_UNMOUNT_RETRIES + " retries. Forcibly killing processes now");
507 }
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800508 sizeArr[sizeArrN++] = i;
509 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_MS,
510 ucb));
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800511 }
512 }
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800513 // Remove already processed elements from list.
514 for (int i = (sizeArrN-1); i >= 0; i--) {
515 mForceUnmounts.remove(sizeArr[i]);
516 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800517 break;
518 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700519 case H_UNMOUNT_MS: {
San Mehata5078592010-03-25 09:36:54 -0700520 if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_MS");
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800521 UnmountCallBack ucb = (UnmountCallBack) msg.obj;
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800522 ucb.handleFinished();
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800523 break;
524 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700525 case H_SYSTEM_READY: {
526 try {
527 handleSystemReady();
528 } catch (Exception ex) {
529 Slog.e(TAG, "Boot-time mount exception", ex);
530 }
531 break;
532 }
Christopher Tated417d622013-08-19 16:14:25 -0700533 case H_FSTRIM: {
534 waitForReady();
535 Slog.i(TAG, "Running fstrim idle maintenance");
536 try {
537 // This method must be run on the main (handler) thread,
538 // so it is safe to directly call into vold.
539 mConnector.execute("fstrim", "dotrim");
540 EventLogTags.writeFstrimStart(SystemClock.elapsedRealtime());
541 } catch (NativeDaemonConnectorException ndce) {
542 Slog.e(TAG, "Failed to run fstrim!");
543 }
544 // invoke the completion callback, if any
545 Runnable callback = (Runnable) msg.obj;
546 if (callback != null) {
547 callback.run();
548 }
549 break;
550 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800551 }
552 }
553 };
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700554
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700555 private final Handler mHandler;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800556
Kenny Root51a573c2012-05-17 13:30:28 -0700557 void waitForAsecScan() {
558 waitForLatch(mAsecsScanned);
559 }
560
San Mehat207e5382010-02-04 20:46:54 -0800561 private void waitForReady() {
Kenny Root51a573c2012-05-17 13:30:28 -0700562 waitForLatch(mConnectedSignal);
563 }
564
565 private void waitForLatch(CountDownLatch latch) {
Kenny Root51a573c2012-05-17 13:30:28 -0700566 for (;;) {
567 try {
568 if (latch.await(5000, TimeUnit.MILLISECONDS)) {
San Mehat207e5382010-02-04 20:46:54 -0800569 return;
Kenny Root51a573c2012-05-17 13:30:28 -0700570 } else {
571 Slog.w(TAG, "Thread " + Thread.currentThread().getName()
572 + " still waiting for MountService ready...");
San Mehat207e5382010-02-04 20:46:54 -0800573 }
Kenny Root51a573c2012-05-17 13:30:28 -0700574 } catch (InterruptedException e) {
575 Slog.w(TAG, "Interrupt while waiting for MountService to be ready.");
San Mehat207e5382010-02-04 20:46:54 -0800576 }
San Mehat207e5382010-02-04 20:46:54 -0800577 }
San Mehat1f6301e2010-01-07 22:40:27 -0800578 }
Kenny Root02c87302010-07-01 08:10:18 -0700579
Paul Lawrence945490c2014-03-27 16:37:28 +0000580 private boolean isReady() {
581 try {
582 return mConnectedSignal.await(0, TimeUnit.MILLISECONDS);
583 } catch (InterruptedException e) {
584 return false;
585 }
586 }
587
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700588 private void handleSystemReady() {
589 // Snapshot current volume states since it's not safe to call into vold
590 // while holding locks.
591 final HashMap<String, String> snapshot;
592 synchronized (mVolumesLock) {
593 snapshot = new HashMap<String, String>(mVolumeStates);
594 }
595
596 for (Map.Entry<String, String> entry : snapshot.entrySet()) {
597 final String path = entry.getKey();
598 final String state = entry.getValue();
599
600 if (state.equals(Environment.MEDIA_UNMOUNTED)) {
601 int rc = doMountVolume(path);
602 if (rc != StorageResultCode.OperationSucceeded) {
603 Slog.e(TAG, String.format("Boot-time mount failed (%d)",
604 rc));
605 }
606 } else if (state.equals(Environment.MEDIA_SHARED)) {
607 /*
608 * Bootstrap UMS enabled state since vold indicates
609 * the volume is shared (runtime restart while ums enabled)
610 */
611 notifyVolumeStateChange(null, path, VolumeState.NoMedia,
612 VolumeState.Shared);
613 }
614 }
615
616 // Push mounted state for all emulated storage
617 synchronized (mVolumesLock) {
618 for (StorageVolume volume : mVolumes) {
619 if (volume.isEmulated()) {
620 updatePublicVolumeState(volume, Environment.MEDIA_MOUNTED);
621 }
622 }
623 }
624
625 /*
626 * If UMS was connected on boot, send the connected event
627 * now that we're up.
628 */
629 if (mSendUmsConnectedOnBoot) {
630 sendUmsIntent(true);
631 mSendUmsConnectedOnBoot = false;
632 }
Christopher Tate115afda2014-06-06 19:06:26 -0700633
634 /*
635 * Start scheduling nominally-daily fstrim operations
636 */
637 MountServiceIdler.scheduleIdlePass(mContext);
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700638 }
639
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700640 private final BroadcastReceiver mUserReceiver = new BroadcastReceiver() {
641 @Override
642 public void onReceive(Context context, Intent intent) {
643 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
644 if (userId == -1) return;
645 final UserHandle user = new UserHandle(userId);
646
647 final String action = intent.getAction();
648 if (Intent.ACTION_USER_ADDED.equals(action)) {
649 synchronized (mVolumesLock) {
650 createEmulatedVolumeForUserLocked(user);
651 }
652
653 } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
654 synchronized (mVolumesLock) {
655 final List<StorageVolume> toRemove = Lists.newArrayList();
656 for (StorageVolume volume : mVolumes) {
657 if (user.equals(volume.getOwner())) {
658 toRemove.add(volume);
659 }
660 }
661 for (StorageVolume volume : toRemove) {
662 removeVolumeLocked(volume);
663 }
664 }
665 }
666 }
667 };
668
669 private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
670 @Override
671 public void onReceive(Context context, Intent intent) {
672 boolean available = (intent.getBooleanExtra(UsbManager.USB_CONNECTED, false) &&
673 intent.getBooleanExtra(UsbManager.USB_FUNCTION_MASS_STORAGE, false));
674 notifyShareAvailabilityChange(available);
675 }
676 };
677
San Mehat4270e1e2010-01-29 05:32:19 -0800678 private final class MountServiceBinderListener implements IBinder.DeathRecipient {
679 final IMountServiceListener mListener;
680
681 MountServiceBinderListener(IMountServiceListener listener) {
682 mListener = listener;
Kenny Root02c87302010-07-01 08:10:18 -0700683
San Mehat91c77612010-01-07 10:39:41 -0800684 }
685
San Mehat4270e1e2010-01-29 05:32:19 -0800686 public void binderDied() {
San Mehata5078592010-03-25 09:36:54 -0700687 if (LOCAL_LOGD) Slog.d(TAG, "An IMountServiceListener has died!");
Kenny Roota02b8b02010-08-05 16:14:17 -0700688 synchronized (mListeners) {
San Mehat4270e1e2010-01-29 05:32:19 -0800689 mListeners.remove(this);
690 mListener.asBinder().unlinkToDeath(this, 0);
691 }
692 }
693 }
694
Christopher Tated417d622013-08-19 16:14:25 -0700695 void runIdleMaintenance(Runnable callback) {
696 mHandler.sendMessage(mHandler.obtainMessage(H_FSTRIM, callback));
697 }
698
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800699 private void doShareUnshareVolume(String path, String method, boolean enable) {
San Mehat4270e1e2010-01-29 05:32:19 -0800700 // TODO: Add support for multiple share methods
701 if (!method.equals("ums")) {
702 throw new IllegalArgumentException(String.format("Method %s not supported", method));
703 }
704
San Mehat4270e1e2010-01-29 05:32:19 -0800705 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -0800706 mConnector.execute("volume", enable ? "share" : "unshare", path, method);
San Mehat4270e1e2010-01-29 05:32:19 -0800707 } catch (NativeDaemonConnectorException e) {
San Mehata5078592010-03-25 09:36:54 -0700708 Slog.e(TAG, "Failed to share/unshare", e);
San Mehat4270e1e2010-01-29 05:32:19 -0800709 }
San Mehat4270e1e2010-01-29 05:32:19 -0800710 }
711
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700712 private void updatePublicVolumeState(StorageVolume volume, String state) {
713 final String path = volume.getPath();
714 final String oldState;
715 synchronized (mVolumesLock) {
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400716 oldState = mVolumeStates.put(path, state);
Jeff Sharkey1f706c62013-10-17 10:52:17 -0700717 volume.setState(state);
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400718 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700719
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400720 if (state.equals(oldState)) {
721 Slog.w(TAG, String.format("Duplicate state transition (%s -> %s) for %s",
722 state, state, path));
San Mehat4270e1e2010-01-29 05:32:19 -0800723 return;
724 }
San Mehatb1043402010-02-05 08:26:50 -0800725
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400726 Slog.d(TAG, "volume state changed for " + path + " (" + oldState + " -> " + state + ")");
Kenny Rootaf9d6672010-10-08 09:21:39 -0700727
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700728 // Tell PackageManager about changes to primary volume state, but only
729 // when not emulated.
730 if (volume.isPrimary() && !volume.isEmulated()) {
731 if (Environment.MEDIA_UNMOUNTED.equals(state)) {
732 mPms.updateExternalMediaStatus(false, false);
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400733
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700734 /*
735 * Some OBBs might have been unmounted when this volume was
736 * unmounted, so send a message to the handler to let it know to
737 * remove those from the list of mounted OBBS.
738 */
739 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(
740 OBB_FLUSH_MOUNT_STATE, path));
741 } else if (Environment.MEDIA_MOUNTED.equals(state)) {
742 mPms.updateExternalMediaStatus(true, false);
Mike Lockwood03559752010-07-19 18:25:03 -0400743 }
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800744 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700745
San Mehat4270e1e2010-01-29 05:32:19 -0800746 synchronized (mListeners) {
747 for (int i = mListeners.size() -1; i >= 0; i--) {
748 MountServiceBinderListener bl = mListeners.get(i);
749 try {
San Mehatb1043402010-02-05 08:26:50 -0800750 bl.mListener.onStorageStateChanged(path, oldState, state);
San Mehat4270e1e2010-01-29 05:32:19 -0800751 } catch (RemoteException rex) {
San Mehata5078592010-03-25 09:36:54 -0700752 Slog.e(TAG, "Listener dead");
San Mehat4270e1e2010-01-29 05:32:19 -0800753 mListeners.remove(i);
754 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700755 Slog.e(TAG, "Listener failed", ex);
San Mehat4270e1e2010-01-29 05:32:19 -0800756 }
757 }
758 }
759 }
760
761 /**
San Mehat4270e1e2010-01-29 05:32:19 -0800762 * Callback from NativeDaemonConnector
763 */
764 public void onDaemonConnected() {
765 /*
766 * Since we'll be calling back into the NativeDaemonConnector,
767 * we need to do our work in a new thread.
768 */
Kenny Root51a573c2012-05-17 13:30:28 -0700769 new Thread("MountService#onDaemonConnected") {
Jason parks5af0b912010-11-29 09:05:25 -0600770 @Override
San Mehat4270e1e2010-01-29 05:32:19 -0800771 public void run() {
772 /**
773 * Determine media state and UMS detection status
774 */
San Mehat4270e1e2010-01-29 05:32:19 -0800775 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -0800776 final String[] vols = NativeDaemonEvent.filterMessageList(
JP Abgrall5f054ce2014-07-24 18:19:12 -0700777 mConnector.executeForList("volume", "list", "broadcast"),
Jeff Sharkeydd519fa2011-12-02 14:11:21 -0800778 VoldResponseCode.VolumeListResult);
San Mehat4270e1e2010-01-29 05:32:19 -0800779 for (String volstr : vols) {
780 String[] tok = volstr.split(" ");
781 // FMT: <label> <mountpoint> <state>
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400782 String path = tok[1];
783 String state = Environment.MEDIA_REMOVED;
784
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700785 final StorageVolume volume;
786 synchronized (mVolumesLock) {
787 volume = mVolumesByPath.get(path);
788 }
789
San Mehat4270e1e2010-01-29 05:32:19 -0800790 int st = Integer.parseInt(tok[2]);
791 if (st == VolumeState.NoMedia) {
792 state = Environment.MEDIA_REMOVED;
793 } else if (st == VolumeState.Idle) {
San Mehat207e5382010-02-04 20:46:54 -0800794 state = Environment.MEDIA_UNMOUNTED;
San Mehat4270e1e2010-01-29 05:32:19 -0800795 } else if (st == VolumeState.Mounted) {
796 state = Environment.MEDIA_MOUNTED;
San Mehata5078592010-03-25 09:36:54 -0700797 Slog.i(TAG, "Media already mounted on daemon connection");
San Mehat4270e1e2010-01-29 05:32:19 -0800798 } else if (st == VolumeState.Shared) {
799 state = Environment.MEDIA_SHARED;
San Mehata5078592010-03-25 09:36:54 -0700800 Slog.i(TAG, "Media shared on daemon connection");
San Mehat4270e1e2010-01-29 05:32:19 -0800801 } else {
802 throw new Exception(String.format("Unexpected state %d", st));
803 }
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400804
805 if (state != null) {
806 if (DEBUG_EVENTS) Slog.i(TAG, "Updating valid state " + state);
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700807 updatePublicVolumeState(volume, state);
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400808 }
San Mehat4270e1e2010-01-29 05:32:19 -0800809 }
810 } catch (Exception e) {
San Mehata5078592010-03-25 09:36:54 -0700811 Slog.e(TAG, "Error processing initial volume state", e);
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700812 final StorageVolume primary = getPrimaryPhysicalVolume();
813 if (primary != null) {
814 updatePublicVolumeState(primary, Environment.MEDIA_REMOVED);
815 }
San Mehat4270e1e2010-01-29 05:32:19 -0800816 }
817
San Mehat207e5382010-02-04 20:46:54 -0800818 /*
Jason parks9ed98bc2011-01-17 09:58:35 -0600819 * Now that we've done our initialization, release
San Mehat207e5382010-02-04 20:46:54 -0800820 * the hounds!
821 */
Kenny Root51a573c2012-05-17 13:30:28 -0700822 mConnectedSignal.countDown();
Kenny Root51a573c2012-05-17 13:30:28 -0700823
824 // Let package manager load internal ASECs.
825 mPms.scanAvailableAsecs();
826
827 // Notify people waiting for ASECs to be scanned that it's done.
828 mAsecsScanned.countDown();
San Mehat4270e1e2010-01-29 05:32:19 -0800829 }
830 }.start();
831 }
832
833 /**
San Mehat4270e1e2010-01-29 05:32:19 -0800834 * Callback from NativeDaemonConnector
835 */
Dianne Hackborn77b987f2014-02-26 16:20:52 -0800836 public boolean onCheckHoldWakeLock(int code) {
837 return false;
838 }
839
840 /**
841 * Callback from NativeDaemonConnector
842 */
San Mehat4270e1e2010-01-29 05:32:19 -0800843 public boolean onEvent(int code, String raw, String[] cooked) {
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800844 if (DEBUG_EVENTS) {
845 StringBuilder builder = new StringBuilder();
846 builder.append("onEvent::");
847 builder.append(" raw= " + raw);
848 if (cooked != null) {
849 builder.append(" cooked = " );
850 for (String str : cooked) {
851 builder.append(" " + str);
852 }
853 }
San Mehata5078592010-03-25 09:36:54 -0700854 Slog.i(TAG, builder.toString());
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800855 }
San Mehat4270e1e2010-01-29 05:32:19 -0800856 if (code == VoldResponseCode.VolumeStateChange) {
857 /*
858 * One of the volumes we're managing has changed state.
859 * Format: "NNN Volume <label> <path> state changed
860 * from <old_#> (<old_str>) to <new_#> (<new_str>)"
861 */
862 notifyVolumeStateChange(
863 cooked[2], cooked[3], Integer.parseInt(cooked[7]),
864 Integer.parseInt(cooked[10]));
Jeff Sharkey5aca2b82013-10-16 16:21:54 -0700865 } else if (code == VoldResponseCode.VolumeUuidChange) {
866 // Format: nnn <label> <path> <uuid>
867 final String path = cooked[2];
868 final String uuid = (cooked.length > 3) ? cooked[3] : null;
869
870 final StorageVolume vol = mVolumesByPath.get(path);
871 if (vol != null) {
872 vol.setUuid(uuid);
873 }
874
875 } else if (code == VoldResponseCode.VolumeUserLabelChange) {
876 // Format: nnn <label> <path> <label>
877 final String path = cooked[2];
878 final String userLabel = (cooked.length > 3) ? cooked[3] : null;
879
880 final StorageVolume vol = mVolumesByPath.get(path);
881 if (vol != null) {
882 vol.setUserLabel(userLabel);
883 }
884
San Mehat4270e1e2010-01-29 05:32:19 -0800885 } else if ((code == VoldResponseCode.VolumeDiskInserted) ||
886 (code == VoldResponseCode.VolumeDiskRemoved) ||
887 (code == VoldResponseCode.VolumeBadRemoval)) {
888 // FMT: NNN Volume <label> <mountpoint> disk inserted (<major>:<minor>)
889 // FMT: NNN Volume <label> <mountpoint> disk removed (<major>:<minor>)
890 // FMT: NNN Volume <label> <mountpoint> bad removal (<major>:<minor>)
Mike Lockwooda5250c92011-05-23 13:44:04 -0400891 String action = null;
San Mehat4270e1e2010-01-29 05:32:19 -0800892 final String label = cooked[2];
893 final String path = cooked[3];
894 int major = -1;
895 int minor = -1;
896
897 try {
898 String devComp = cooked[6].substring(1, cooked[6].length() -1);
899 String[] devTok = devComp.split(":");
900 major = Integer.parseInt(devTok[0]);
901 minor = Integer.parseInt(devTok[1]);
902 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700903 Slog.e(TAG, "Failed to parse major/minor", ex);
San Mehat4270e1e2010-01-29 05:32:19 -0800904 }
905
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700906 final StorageVolume volume;
907 final String state;
908 synchronized (mVolumesLock) {
909 volume = mVolumesByPath.get(path);
910 state = mVolumeStates.get(path);
911 }
912
San Mehat4270e1e2010-01-29 05:32:19 -0800913 if (code == VoldResponseCode.VolumeDiskInserted) {
Dianne Hackborn8d044e82013-04-30 17:24:15 -0700914 new Thread("MountService#VolumeDiskInserted") {
Jason parks5af0b912010-11-29 09:05:25 -0600915 @Override
San Mehat4270e1e2010-01-29 05:32:19 -0800916 public void run() {
917 try {
918 int rc;
San Mehatb1043402010-02-05 08:26:50 -0800919 if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {
San Mehata5078592010-03-25 09:36:54 -0700920 Slog.w(TAG, String.format("Insertion mount failed (%d)", rc));
San Mehat4270e1e2010-01-29 05:32:19 -0800921 }
922 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700923 Slog.w(TAG, "Failed to mount media on insertion", ex);
San Mehat4270e1e2010-01-29 05:32:19 -0800924 }
925 }
926 }.start();
927 } else if (code == VoldResponseCode.VolumeDiskRemoved) {
928 /*
929 * This event gets trumped if we're already in BAD_REMOVAL state
930 */
931 if (getVolumeState(path).equals(Environment.MEDIA_BAD_REMOVAL)) {
932 return true;
933 }
934 /* Send the media unmounted event first */
San Mehata5078592010-03-25 09:36:54 -0700935 if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700936 updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
Johan Redestig0464c072014-01-18 22:46:56 +0100937 sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, volume, UserHandle.ALL);
San Mehat4270e1e2010-01-29 05:32:19 -0800938
San Mehata5078592010-03-25 09:36:54 -0700939 if (DEBUG_EVENTS) Slog.i(TAG, "Sending media removed");
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700940 updatePublicVolumeState(volume, Environment.MEDIA_REMOVED);
Mike Lockwooda5250c92011-05-23 13:44:04 -0400941 action = Intent.ACTION_MEDIA_REMOVED;
San Mehat4270e1e2010-01-29 05:32:19 -0800942 } else if (code == VoldResponseCode.VolumeBadRemoval) {
San Mehata5078592010-03-25 09:36:54 -0700943 if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
San Mehat4270e1e2010-01-29 05:32:19 -0800944 /* Send the media unmounted event first */
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700945 updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
Christian Beckf503c8f2013-05-20 08:42:45 +0200946 sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, volume, UserHandle.ALL);
San Mehat4270e1e2010-01-29 05:32:19 -0800947
San Mehata5078592010-03-25 09:36:54 -0700948 if (DEBUG_EVENTS) Slog.i(TAG, "Sending media bad removal");
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700949 updatePublicVolumeState(volume, Environment.MEDIA_BAD_REMOVAL);
Mike Lockwooda5250c92011-05-23 13:44:04 -0400950 action = Intent.ACTION_MEDIA_BAD_REMOVAL;
Svetoslavf23b64d2013-04-25 14:45:54 -0700951 } else if (code == VoldResponseCode.FstrimCompleted) {
Svetoslav9e814a82013-04-30 10:43:56 -0700952 EventLogTags.writeFstrimFinish(SystemClock.elapsedRealtime());
San Mehat4270e1e2010-01-29 05:32:19 -0800953 } else {
San Mehata5078592010-03-25 09:36:54 -0700954 Slog.e(TAG, String.format("Unknown code {%d}", code));
San Mehat4270e1e2010-01-29 05:32:19 -0800955 }
Mike Lockwooda5250c92011-05-23 13:44:04 -0400956
957 if (action != null) {
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700958 sendStorageIntent(action, volume, UserHandle.ALL);
Mike Lockwooda5250c92011-05-23 13:44:04 -0400959 }
San Mehat4270e1e2010-01-29 05:32:19 -0800960 } else {
961 return false;
962 }
963
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400964 return true;
San Mehat4270e1e2010-01-29 05:32:19 -0800965 }
966
San Mehat207e5382010-02-04 20:46:54 -0800967 private void notifyVolumeStateChange(String label, String path, int oldState, int newState) {
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700968 final StorageVolume volume;
969 final String state;
970 synchronized (mVolumesLock) {
971 volume = mVolumesByPath.get(path);
972 state = getVolumeState(path);
973 }
974
975 if (DEBUG_EVENTS) Slog.i(TAG, "notifyVolumeStateChange::" + state);
San Mehat4270e1e2010-01-29 05:32:19 -0800976
Mike Lockwooda5250c92011-05-23 13:44:04 -0400977 String action = null;
San Mehat4270e1e2010-01-29 05:32:19 -0800978
Mike Lockwoodbf2dd442010-03-03 06:16:52 -0500979 if (oldState == VolumeState.Shared && newState != oldState) {
San Mehata5078592010-03-25 09:36:54 -0700980 if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_UNSHARED intent");
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700981 sendStorageIntent(Intent.ACTION_MEDIA_UNSHARED, volume, UserHandle.ALL);
Mike Lockwoodbf2dd442010-03-03 06:16:52 -0500982 }
983
San Mehat4270e1e2010-01-29 05:32:19 -0800984 if (newState == VolumeState.Init) {
985 } else if (newState == VolumeState.NoMedia) {
986 // NoMedia is handled via Disk Remove events
987 } else if (newState == VolumeState.Idle) {
988 /*
989 * Don't notify if we're in BAD_REMOVAL, NOFS, UNMOUNTABLE, or
990 * if we're in the process of enabling UMS
991 */
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700992 if (!state.equals(
993 Environment.MEDIA_BAD_REMOVAL) && !state.equals(
994 Environment.MEDIA_NOFS) && !state.equals(
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800995 Environment.MEDIA_UNMOUNTABLE) && !getUmsEnabling()) {
San Mehata5078592010-03-25 09:36:54 -0700996 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state for media bad removal nofs and unmountable");
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700997 updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
Mike Lockwooda5250c92011-05-23 13:44:04 -0400998 action = Intent.ACTION_MEDIA_UNMOUNTED;
San Mehat4270e1e2010-01-29 05:32:19 -0800999 }
1000 } else if (newState == VolumeState.Pending) {
1001 } else if (newState == VolumeState.Checking) {
San Mehata5078592010-03-25 09:36:54 -07001002 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state checking");
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001003 updatePublicVolumeState(volume, Environment.MEDIA_CHECKING);
Mike Lockwooda5250c92011-05-23 13:44:04 -04001004 action = Intent.ACTION_MEDIA_CHECKING;
San Mehat4270e1e2010-01-29 05:32:19 -08001005 } else if (newState == VolumeState.Mounted) {
San Mehata5078592010-03-25 09:36:54 -07001006 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state mounted");
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001007 updatePublicVolumeState(volume, Environment.MEDIA_MOUNTED);
Mike Lockwooda5250c92011-05-23 13:44:04 -04001008 action = Intent.ACTION_MEDIA_MOUNTED;
San Mehat4270e1e2010-01-29 05:32:19 -08001009 } else if (newState == VolumeState.Unmounting) {
Mike Lockwooda5250c92011-05-23 13:44:04 -04001010 action = Intent.ACTION_MEDIA_EJECT;
San Mehat4270e1e2010-01-29 05:32:19 -08001011 } else if (newState == VolumeState.Formatting) {
1012 } else if (newState == VolumeState.Shared) {
San Mehata5078592010-03-25 09:36:54 -07001013 if (DEBUG_EVENTS) Slog.i(TAG, "Updating volume state media mounted");
San Mehat4270e1e2010-01-29 05:32:19 -08001014 /* Send the media unmounted event first */
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001015 updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
1016 sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, volume, UserHandle.ALL);
San Mehat4270e1e2010-01-29 05:32:19 -08001017
San Mehata5078592010-03-25 09:36:54 -07001018 if (DEBUG_EVENTS) Slog.i(TAG, "Updating media shared");
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001019 updatePublicVolumeState(volume, Environment.MEDIA_SHARED);
Mike Lockwooda5250c92011-05-23 13:44:04 -04001020 action = Intent.ACTION_MEDIA_SHARED;
San Mehata5078592010-03-25 09:36:54 -07001021 if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_SHARED intent");
San Mehat4270e1e2010-01-29 05:32:19 -08001022 } else if (newState == VolumeState.SharedMnt) {
San Mehata5078592010-03-25 09:36:54 -07001023 Slog.e(TAG, "Live shared mounts not supported yet!");
San Mehat4270e1e2010-01-29 05:32:19 -08001024 return;
1025 } else {
San Mehata5078592010-03-25 09:36:54 -07001026 Slog.e(TAG, "Unhandled VolumeState {" + newState + "}");
San Mehat4270e1e2010-01-29 05:32:19 -08001027 }
1028
Mike Lockwooda5250c92011-05-23 13:44:04 -04001029 if (action != null) {
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001030 sendStorageIntent(action, volume, UserHandle.ALL);
San Mehat4270e1e2010-01-29 05:32:19 -08001031 }
1032 }
1033
San Mehat207e5382010-02-04 20:46:54 -08001034 private int doMountVolume(String path) {
San Mehatb1043402010-02-05 08:26:50 -08001035 int rc = StorageResultCode.OperationSucceeded;
San Mehat207e5382010-02-04 20:46:54 -08001036
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001037 final StorageVolume volume;
1038 synchronized (mVolumesLock) {
1039 volume = mVolumesByPath.get(path);
1040 }
1041
Emily Bernier92aa5a22014-07-07 10:11:48 -04001042 if (!volume.isEmulated() && hasUserRestriction(UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA)) {
1043 Slog.w(TAG, "User has restriction DISALLOW_MOUNT_PHYSICAL_MEDIA; cannot mount volume.");
1044 return StorageResultCode.OperationFailedInternalError;
1045 }
1046
San Mehata5078592010-03-25 09:36:54 -07001047 if (DEBUG_EVENTS) Slog.i(TAG, "doMountVolume: Mouting " + path);
San Mehat207e5382010-02-04 20:46:54 -08001048 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001049 mConnector.execute("volume", "mount", path);
San Mehat207e5382010-02-04 20:46:54 -08001050 } catch (NativeDaemonConnectorException e) {
1051 /*
1052 * Mount failed for some reason
1053 */
Mike Lockwooda5250c92011-05-23 13:44:04 -04001054 String action = null;
San Mehat207e5382010-02-04 20:46:54 -08001055 int code = e.getCode();
1056 if (code == VoldResponseCode.OpFailedNoMedia) {
1057 /*
1058 * Attempt to mount but no media inserted
1059 */
San Mehatb1043402010-02-05 08:26:50 -08001060 rc = StorageResultCode.OperationFailedNoMedia;
San Mehat207e5382010-02-04 20:46:54 -08001061 } else if (code == VoldResponseCode.OpFailedMediaBlank) {
San Mehata5078592010-03-25 09:36:54 -07001062 if (DEBUG_EVENTS) Slog.i(TAG, " updating volume state :: media nofs");
San Mehat207e5382010-02-04 20:46:54 -08001063 /*
1064 * Media is blank or does not contain a supported filesystem
1065 */
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001066 updatePublicVolumeState(volume, Environment.MEDIA_NOFS);
Mike Lockwooda5250c92011-05-23 13:44:04 -04001067 action = Intent.ACTION_MEDIA_NOFS;
San Mehatb1043402010-02-05 08:26:50 -08001068 rc = StorageResultCode.OperationFailedMediaBlank;
San Mehat207e5382010-02-04 20:46:54 -08001069 } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
San Mehata5078592010-03-25 09:36:54 -07001070 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state media corrupt");
San Mehat207e5382010-02-04 20:46:54 -08001071 /*
1072 * Volume consistency check failed
1073 */
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001074 updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTABLE);
Mike Lockwooda5250c92011-05-23 13:44:04 -04001075 action = Intent.ACTION_MEDIA_UNMOUNTABLE;
San Mehatb1043402010-02-05 08:26:50 -08001076 rc = StorageResultCode.OperationFailedMediaCorrupt;
San Mehat207e5382010-02-04 20:46:54 -08001077 } else {
San Mehatb1043402010-02-05 08:26:50 -08001078 rc = StorageResultCode.OperationFailedInternalError;
San Mehat207e5382010-02-04 20:46:54 -08001079 }
1080
1081 /*
1082 * Send broadcast intent (if required for the failure)
1083 */
Mike Lockwooda5250c92011-05-23 13:44:04 -04001084 if (action != null) {
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001085 sendStorageIntent(action, volume, UserHandle.ALL);
San Mehat207e5382010-02-04 20:46:54 -08001086 }
1087 }
1088
1089 return rc;
1090 }
1091
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -08001092 /*
1093 * If force is not set, we do not unmount if there are
1094 * processes holding references to the volume about to be unmounted.
1095 * If force is set, all the processes holding references need to be
1096 * killed via the ActivityManager before actually unmounting the volume.
1097 * This might even take a while and might be retried after timed delays
1098 * to make sure we dont end up in an instable state and kill some core
1099 * processes.
Ben Komalo13c71972011-09-07 16:35:56 -07001100 * If removeEncryption is set, force is implied, and the system will remove any encryption
1101 * mapping set on the volume when unmounting.
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -08001102 */
Ben Komalo13c71972011-09-07 16:35:56 -07001103 private int doUnmountVolume(String path, boolean force, boolean removeEncryption) {
San Mehat59443a62010-02-09 13:28:45 -08001104 if (!getVolumeState(path).equals(Environment.MEDIA_MOUNTED)) {
San Mehat207e5382010-02-04 20:46:54 -08001105 return VoldResponseCode.OpFailedVolNotMounted;
1106 }
Kenny Rootaa485402010-09-14 14:49:41 -07001107
1108 /*
1109 * Force a GC to make sure AssetManagers in other threads of the
1110 * system_server are cleaned up. We have to do this since AssetManager
1111 * instances are kept as a WeakReference and it's possible we have files
1112 * open on the external storage.
1113 */
1114 Runtime.getRuntime().gc();
1115
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -08001116 // Redundant probably. But no harm in updating state again.
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -07001117 mPms.updateExternalMediaStatus(false, false);
San Mehat207e5382010-02-04 20:46:54 -08001118 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001119 final Command cmd = new Command("volume", "unmount", path);
1120 if (removeEncryption) {
1121 cmd.appendArg("force_and_revert");
1122 } else if (force) {
1123 cmd.appendArg("force");
1124 }
1125 mConnector.execute(cmd);
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -07001126 // We unmounted the volume. None of the asec containers are available now.
1127 synchronized (mAsecMountSet) {
1128 mAsecMountSet.clear();
1129 }
San Mehatb1043402010-02-05 08:26:50 -08001130 return StorageResultCode.OperationSucceeded;
San Mehat207e5382010-02-04 20:46:54 -08001131 } catch (NativeDaemonConnectorException e) {
1132 // Don't worry about mismatch in PackageManager since the
1133 // call back will handle the status changes any way.
1134 int code = e.getCode();
1135 if (code == VoldResponseCode.OpFailedVolNotMounted) {
San Mehata181b212010-02-11 06:50:20 -08001136 return StorageResultCode.OperationFailedStorageNotMounted;
San Mehatd9709982010-02-18 11:43:03 -08001137 } else if (code == VoldResponseCode.OpFailedStorageBusy) {
1138 return StorageResultCode.OperationFailedStorageBusy;
San Mehat207e5382010-02-04 20:46:54 -08001139 } else {
San Mehatb1043402010-02-05 08:26:50 -08001140 return StorageResultCode.OperationFailedInternalError;
San Mehat207e5382010-02-04 20:46:54 -08001141 }
1142 }
1143 }
1144
1145 private int doFormatVolume(String path) {
1146 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001147 mConnector.execute("volume", "format", path);
San Mehatb1043402010-02-05 08:26:50 -08001148 return StorageResultCode.OperationSucceeded;
San Mehat207e5382010-02-04 20:46:54 -08001149 } catch (NativeDaemonConnectorException e) {
1150 int code = e.getCode();
1151 if (code == VoldResponseCode.OpFailedNoMedia) {
San Mehatb1043402010-02-05 08:26:50 -08001152 return StorageResultCode.OperationFailedNoMedia;
San Mehat207e5382010-02-04 20:46:54 -08001153 } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
San Mehatb1043402010-02-05 08:26:50 -08001154 return StorageResultCode.OperationFailedMediaCorrupt;
San Mehat207e5382010-02-04 20:46:54 -08001155 } else {
San Mehatb1043402010-02-05 08:26:50 -08001156 return StorageResultCode.OperationFailedInternalError;
San Mehat207e5382010-02-04 20:46:54 -08001157 }
1158 }
1159 }
1160
San Mehatb1043402010-02-05 08:26:50 -08001161 private boolean doGetVolumeShared(String path, String method) {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001162 final NativeDaemonEvent event;
Kenny Roota80ce062010-06-01 13:23:53 -07001163 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001164 event = mConnector.execute("volume", "shared", path, method);
Kenny Roota80ce062010-06-01 13:23:53 -07001165 } catch (NativeDaemonConnectorException ex) {
1166 Slog.e(TAG, "Failed to read response to volume shared " + path + " " + method);
1167 return false;
1168 }
San Mehatb1043402010-02-05 08:26:50 -08001169
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001170 if (event.getCode() == VoldResponseCode.ShareEnabledResult) {
1171 return event.getMessage().endsWith("enabled");
1172 } else {
1173 return false;
San Mehatb1043402010-02-05 08:26:50 -08001174 }
San Mehatb1043402010-02-05 08:26:50 -08001175 }
1176
Mike Lockwoodecedfdc2011-06-08 15:11:59 -07001177 private void notifyShareAvailabilityChange(final boolean avail) {
San Mehat4270e1e2010-01-29 05:32:19 -08001178 synchronized (mListeners) {
Mike Lockwoodecedfdc2011-06-08 15:11:59 -07001179 mUmsAvailable = avail;
San Mehat4270e1e2010-01-29 05:32:19 -08001180 for (int i = mListeners.size() -1; i >= 0; i--) {
1181 MountServiceBinderListener bl = mListeners.get(i);
1182 try {
San Mehatb1043402010-02-05 08:26:50 -08001183 bl.mListener.onUsbMassStorageConnectionChanged(avail);
San Mehat4270e1e2010-01-29 05:32:19 -08001184 } catch (RemoteException rex) {
San Mehata5078592010-03-25 09:36:54 -07001185 Slog.e(TAG, "Listener dead");
San Mehat4270e1e2010-01-29 05:32:19 -08001186 mListeners.remove(i);
1187 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -07001188 Slog.e(TAG, "Listener failed", ex);
San Mehat4270e1e2010-01-29 05:32:19 -08001189 }
1190 }
1191 }
1192
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001193 if (mSystemReady == true) {
San Mehat6a965af22010-02-24 17:47:30 -08001194 sendUmsIntent(avail);
1195 } else {
1196 mSendUmsConnectedOnBoot = avail;
San Mehat4270e1e2010-01-29 05:32:19 -08001197 }
San Mehat2fe718a2010-03-11 12:01:49 -08001198
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001199 final StorageVolume primary = getPrimaryPhysicalVolume();
1200 if (avail == false && primary != null
1201 && Environment.MEDIA_SHARED.equals(getVolumeState(primary.getPath()))) {
1202 final String path = primary.getPath();
San Mehat2fe718a2010-03-11 12:01:49 -08001203 /*
1204 * USB mass storage disconnected while enabled
1205 */
Dianne Hackborn8d044e82013-04-30 17:24:15 -07001206 new Thread("MountService#AvailabilityChange") {
Jason parks5af0b912010-11-29 09:05:25 -06001207 @Override
San Mehat2fe718a2010-03-11 12:01:49 -08001208 public void run() {
1209 try {
1210 int rc;
San Mehata5078592010-03-25 09:36:54 -07001211 Slog.w(TAG, "Disabling UMS after cable disconnect");
San Mehat2fe718a2010-03-11 12:01:49 -08001212 doShareUnshareVolume(path, "ums", false);
1213 if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {
San Mehata5078592010-03-25 09:36:54 -07001214 Slog.e(TAG, String.format(
San Mehat2fe718a2010-03-11 12:01:49 -08001215 "Failed to remount {%s} on UMS enabled-disconnect (%d)",
1216 path, rc));
1217 }
1218 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -07001219 Slog.w(TAG, "Failed to mount media on UMS enabled-disconnect", ex);
San Mehat2fe718a2010-03-11 12:01:49 -08001220 }
1221 }
1222 }.start();
1223 }
San Mehat4270e1e2010-01-29 05:32:19 -08001224 }
1225
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001226 private void sendStorageIntent(String action, StorageVolume volume, UserHandle user) {
1227 final Intent intent = new Intent(action, Uri.parse("file://" + volume.getPath()));
1228 intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, volume);
Johan Redestig0464c072014-01-18 22:46:56 +01001229 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001230 Slog.d(TAG, "sendStorageIntent " + intent + " to " + user);
1231 mContext.sendBroadcastAsUser(intent, user);
Mike Lockwooda5250c92011-05-23 13:44:04 -04001232 }
1233
San Mehat6a965af22010-02-24 17:47:30 -08001234 private void sendUmsIntent(boolean c) {
Dianne Hackborn5ac72a22012-08-29 18:32:08 -07001235 mContext.sendBroadcastAsUser(
1236 new Intent((c ? Intent.ACTION_UMS_CONNECTED : Intent.ACTION_UMS_DISCONNECTED)),
1237 UserHandle.ALL);
San Mehat6a965af22010-02-24 17:47:30 -08001238 }
1239
San Mehat207e5382010-02-04 20:46:54 -08001240 private void validatePermission(String perm) {
San Mehat4270e1e2010-01-29 05:32:19 -08001241 if (mContext.checkCallingOrSelfPermission(perm) != PackageManager.PERMISSION_GRANTED) {
1242 throw new SecurityException(String.format("Requires %s permission", perm));
1243 }
1244 }
1245
Emily Bernier92aa5a22014-07-07 10:11:48 -04001246 private boolean hasUserRestriction(String restriction) {
1247 UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
1248 return um.hasUserRestriction(restriction, Binder.getCallingUserHandle());
1249 }
1250
1251 private void validateUserRestriction(String restriction) {
1252 if (hasUserRestriction(restriction)) {
1253 throw new SecurityException("User has restriction " + restriction);
1254 }
1255 }
1256
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001257 // Storage list XML tags
1258 private static final String TAG_STORAGE_LIST = "StorageList";
1259 private static final String TAG_STORAGE = "storage";
1260
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001261 private void readStorageListLocked() {
1262 mVolumes.clear();
1263 mVolumeStates.clear();
1264
Fabrice Di Meglio13fe2a52012-05-18 18:08:58 -07001265 Resources resources = mContext.getResources();
1266
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001267 int id = com.android.internal.R.xml.storage_list;
1268 XmlResourceParser parser = resources.getXml(id);
1269 AttributeSet attrs = Xml.asAttributeSet(parser);
1270
1271 try {
1272 XmlUtils.beginDocument(parser, TAG_STORAGE_LIST);
1273 while (true) {
1274 XmlUtils.nextElement(parser);
1275
1276 String element = parser.getName();
1277 if (element == null) break;
1278
1279 if (TAG_STORAGE.equals(element)) {
1280 TypedArray a = resources.obtainAttributes(attrs,
1281 com.android.internal.R.styleable.Storage);
1282
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001283 String path = a.getString(
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001284 com.android.internal.R.styleable.Storage_mountPoint);
Fabrice Di Meglio13fe2a52012-05-18 18:08:58 -07001285 int descriptionId = a.getResourceId(
1286 com.android.internal.R.styleable.Storage_storageDescription, -1);
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001287 CharSequence description = a.getText(
1288 com.android.internal.R.styleable.Storage_storageDescription);
1289 boolean primary = a.getBoolean(
1290 com.android.internal.R.styleable.Storage_primary, false);
1291 boolean removable = a.getBoolean(
1292 com.android.internal.R.styleable.Storage_removable, false);
1293 boolean emulated = a.getBoolean(
1294 com.android.internal.R.styleable.Storage_emulated, false);
1295 int mtpReserve = a.getInt(
1296 com.android.internal.R.styleable.Storage_mtpReserve, 0);
Mike Lockwood8e8b2802011-06-07 08:03:33 -07001297 boolean allowMassStorage = a.getBoolean(
1298 com.android.internal.R.styleable.Storage_allowMassStorage, false);
Mike Lockwood7a59dd22011-07-11 09:18:03 -04001299 // resource parser does not support longs, so XML value is in megabytes
1300 long maxFileSize = a.getInt(
1301 com.android.internal.R.styleable.Storage_maxFileSize, 0) * 1024L * 1024L;
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001302
1303 Slog.d(TAG, "got storage path: " + path + " description: " + description +
1304 " primary: " + primary + " removable: " + removable +
Mike Lockwood8e8b2802011-06-07 08:03:33 -07001305 " emulated: " + emulated + " mtpReserve: " + mtpReserve +
Mike Lockwood7a59dd22011-07-11 09:18:03 -04001306 " allowMassStorage: " + allowMassStorage +
1307 " maxFileSize: " + maxFileSize);
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001308
1309 if (emulated) {
1310 // For devices with emulated storage, we create separate
1311 // volumes for each known user.
1312 mEmulatedTemplate = new StorageVolume(null, descriptionId, true, false,
1313 true, mtpReserve, false, maxFileSize, null);
1314
1315 final UserManagerService userManager = UserManagerService.getInstance();
Amith Yamasani920ace02012-09-20 22:15:37 -07001316 for (UserInfo user : userManager.getUsers(false)) {
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001317 createEmulatedVolumeForUserLocked(user.getUserHandle());
1318 }
1319
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001320 } else {
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001321 if (path == null || description == null) {
1322 Slog.e(TAG, "Missing storage path or description in readStorageList");
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001323 } else {
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001324 final StorageVolume volume = new StorageVolume(new File(path),
1325 descriptionId, primary, removable, emulated, mtpReserve,
1326 allowMassStorage, maxFileSize, null);
1327 addVolumeLocked(volume);
Jeff Sharkey44cbdec2013-10-07 16:49:47 -07001328
1329 // Until we hear otherwise, treat as unmounted
1330 mVolumeStates.put(volume.getPath(), Environment.MEDIA_UNMOUNTED);
Jeff Sharkey1f706c62013-10-17 10:52:17 -07001331 volume.setState(Environment.MEDIA_UNMOUNTED);
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001332 }
1333 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001334
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001335 a.recycle();
1336 }
1337 }
1338 } catch (XmlPullParserException e) {
1339 throw new RuntimeException(e);
1340 } catch (IOException e) {
1341 throw new RuntimeException(e);
1342 } finally {
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001343 // Compute storage ID for each physical volume; emulated storage is
1344 // always 0 when defined.
1345 int index = isExternalStorageEmulated() ? 1 : 0;
1346 for (StorageVolume volume : mVolumes) {
1347 if (!volume.isEmulated()) {
1348 volume.setStorageId(index++);
1349 }
Mike Lockwoodfbfe5552011-05-17 17:19:37 -04001350 }
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001351 parser.close();
1352 }
1353 }
1354
San Mehat4270e1e2010-01-29 05:32:19 -08001355 /**
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001356 * Create and add new {@link StorageVolume} for given {@link UserHandle}
1357 * using {@link #mEmulatedTemplate} as template.
1358 */
1359 private void createEmulatedVolumeForUserLocked(UserHandle user) {
1360 if (mEmulatedTemplate == null) {
1361 throw new IllegalStateException("Missing emulated volume multi-user template");
1362 }
1363
1364 final UserEnvironment userEnv = new UserEnvironment(user.getIdentifier());
1365 final File path = userEnv.getExternalStorageDirectory();
1366 final StorageVolume volume = StorageVolume.fromTemplate(mEmulatedTemplate, path, user);
1367 volume.setStorageId(0);
1368 addVolumeLocked(volume);
1369
1370 if (mSystemReady) {
1371 updatePublicVolumeState(volume, Environment.MEDIA_MOUNTED);
1372 } else {
1373 // Place stub status for early callers to find
1374 mVolumeStates.put(volume.getPath(), Environment.MEDIA_MOUNTED);
Jeff Sharkey1f706c62013-10-17 10:52:17 -07001375 volume.setState(Environment.MEDIA_MOUNTED);
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001376 }
1377 }
1378
1379 private void addVolumeLocked(StorageVolume volume) {
1380 Slog.d(TAG, "addVolumeLocked() " + volume);
1381 mVolumes.add(volume);
1382 final StorageVolume existing = mVolumesByPath.put(volume.getPath(), volume);
1383 if (existing != null) {
1384 throw new IllegalStateException(
1385 "Volume at " + volume.getPath() + " already exists: " + existing);
1386 }
1387 }
1388
1389 private void removeVolumeLocked(StorageVolume volume) {
1390 Slog.d(TAG, "removeVolumeLocked() " + volume);
1391 mVolumes.remove(volume);
1392 mVolumesByPath.remove(volume.getPath());
1393 mVolumeStates.remove(volume.getPath());
1394 }
1395
1396 private StorageVolume getPrimaryPhysicalVolume() {
1397 synchronized (mVolumesLock) {
1398 for (StorageVolume volume : mVolumes) {
1399 if (volume.isPrimary() && !volume.isEmulated()) {
1400 return volume;
1401 }
1402 }
1403 }
1404 return null;
1405 }
1406
1407 /**
San Mehat207e5382010-02-04 20:46:54 -08001408 * Constructs a new MountService instance
1409 *
1410 * @param context Binder context for this service
1411 */
1412 public MountService(Context context) {
Christopher Tated417d622013-08-19 16:14:25 -07001413 sSelf = this;
1414
San Mehat207e5382010-02-04 20:46:54 -08001415 mContext = context;
1416
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001417 synchronized (mVolumesLock) {
1418 readStorageListLocked();
Mike Lockwood03559752010-07-19 18:25:03 -04001419 }
1420
San Mehat207e5382010-02-04 20:46:54 -08001421 // XXX: This will go away soon in favor of IMountServiceObserver
1422 mPms = (PackageManagerService) ServiceManager.getService("package");
1423
Dianne Hackbornefa92b22013-05-03 14:11:43 -07001424 HandlerThread hthread = new HandlerThread(TAG);
1425 hthread.start();
1426 mHandler = new MountServiceHandler(hthread.getLooper());
Daniel Sandler5f27ef42010-03-16 15:42:02 -04001427
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001428 // Watch for user changes
1429 final IntentFilter userFilter = new IntentFilter();
1430 userFilter.addAction(Intent.ACTION_USER_ADDED);
1431 userFilter.addAction(Intent.ACTION_USER_REMOVED);
1432 mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);
1433
1434 // Watch for USB changes on primary volume
1435 final StorageVolume primary = getPrimaryPhysicalVolume();
1436 if (primary != null && primary.allowMassStorage()) {
1437 mContext.registerReceiver(
1438 mUsbReceiver, new IntentFilter(UsbManager.ACTION_USB_STATE), null, mHandler);
1439 }
1440
Kenny Roota02b8b02010-08-05 16:14:17 -07001441 // Add OBB Action Handler to MountService thread.
Dianne Hackborn8d044e82013-04-30 17:24:15 -07001442 mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper());
Kenny Roota02b8b02010-08-05 16:14:17 -07001443
Marco Nelissenc34ebce2010-02-18 13:39:41 -08001444 /*
Kenny Root305bcbf2010-09-03 07:56:38 -07001445 * Create the connection to vold with a maximum queue of twice the
1446 * amount of containers we'd ever expect to have. This keeps an
1447 * "asec list" from blocking a thread repeatedly.
1448 */
Dianne Hackborn77b987f2014-02-26 16:20:52 -08001449 mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25,
1450 null);
Kenny Root51a573c2012-05-17 13:30:28 -07001451
Kenny Root305bcbf2010-09-03 07:56:38 -07001452 Thread thread = new Thread(mConnector, VOLD_TAG);
San Mehat207e5382010-02-04 20:46:54 -08001453 thread.start();
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -07001454
Kenny Root07714d42011-08-17 17:49:28 -07001455 // Add ourself to the Watchdog monitors if enabled.
1456 if (WATCHDOG_ENABLE) {
1457 Watchdog.getInstance().addMonitor(this);
1458 }
San Mehat207e5382010-02-04 20:46:54 -08001459 }
1460
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001461 public void systemReady() {
1462 mSystemReady = true;
1463 mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget();
1464 }
1465
San Mehat207e5382010-02-04 20:46:54 -08001466 /**
San Mehat4270e1e2010-01-29 05:32:19 -08001467 * Exposed API calls below here
1468 */
1469
1470 public void registerListener(IMountServiceListener listener) {
1471 synchronized (mListeners) {
1472 MountServiceBinderListener bl = new MountServiceBinderListener(listener);
1473 try {
1474 listener.asBinder().linkToDeath(bl, 0);
1475 mListeners.add(bl);
1476 } catch (RemoteException rex) {
San Mehata5078592010-03-25 09:36:54 -07001477 Slog.e(TAG, "Failed to link to listener death");
San Mehat4270e1e2010-01-29 05:32:19 -08001478 }
1479 }
1480 }
1481
1482 public void unregisterListener(IMountServiceListener listener) {
1483 synchronized (mListeners) {
1484 for(MountServiceBinderListener bl : mListeners) {
Niklas Brunlidd64fe0f2013-07-05 08:54:03 +02001485 if (bl.mListener.asBinder() == listener.asBinder()) {
San Mehat4270e1e2010-01-29 05:32:19 -08001486 mListeners.remove(mListeners.indexOf(bl));
Vairavan Srinivasan5c25a2d2012-01-24 08:22:14 -08001487 listener.asBinder().unlinkToDeath(bl, 0);
San Mehat4270e1e2010-01-29 05:32:19 -08001488 return;
1489 }
1490 }
1491 }
1492 }
1493
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -08001494 public void shutdown(final IMountShutdownObserver observer) {
San Mehat4270e1e2010-01-29 05:32:19 -08001495 validatePermission(android.Manifest.permission.SHUTDOWN);
1496
San Mehata5078592010-03-25 09:36:54 -07001497 Slog.i(TAG, "Shutting down");
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001498 synchronized (mVolumesLock) {
Rickard Helldin5fb5df02014-02-07 09:35:04 +01001499 // Get all volumes to be unmounted.
1500 MountShutdownLatch mountShutdownLatch = new MountShutdownLatch(observer,
1501 mVolumeStates.size());
1502
Mike Lockwood7fa24aa2011-03-23 14:52:34 -04001503 for (String path : mVolumeStates.keySet()) {
1504 String state = mVolumeStates.get(path);
San Mehat4270e1e2010-01-29 05:32:19 -08001505
Mike Lockwood7fa24aa2011-03-23 14:52:34 -04001506 if (state.equals(Environment.MEDIA_SHARED)) {
1507 /*
1508 * If the media is currently shared, unshare it.
1509 * XXX: This is still dangerous!. We should not
1510 * be rebooting at *all* if UMS is enabled, since
1511 * the UMS host could have dirty FAT cache entries
1512 * yet to flush.
1513 */
1514 setUsbMassStorageEnabled(false);
1515 } else if (state.equals(Environment.MEDIA_CHECKING)) {
1516 /*
1517 * If the media is being checked, then we need to wait for
1518 * it to complete before being able to proceed.
1519 */
1520 // XXX: @hackbod - Should we disable the ANR timer here?
1521 int retries = 30;
1522 while (state.equals(Environment.MEDIA_CHECKING) && (retries-- >=0)) {
1523 try {
1524 Thread.sleep(1000);
1525 } catch (InterruptedException iex) {
1526 Slog.e(TAG, "Interrupted while waiting for media", iex);
1527 break;
1528 }
1529 state = Environment.getExternalStorageState();
1530 }
1531 if (retries == 0) {
1532 Slog.e(TAG, "Timed out waiting for media to check");
1533 }
San Mehat91c77612010-01-07 10:39:41 -08001534 }
San Mehat91c77612010-01-07 10:39:41 -08001535
Mike Lockwood7fa24aa2011-03-23 14:52:34 -04001536 if (state.equals(Environment.MEDIA_MOUNTED)) {
1537 // Post a unmount message.
Rickard Helldin5fb5df02014-02-07 09:35:04 +01001538 ShutdownCallBack ucb = new ShutdownCallBack(path, mountShutdownLatch);
Mike Lockwood7fa24aa2011-03-23 14:52:34 -04001539 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
1540 } else if (observer != null) {
1541 /*
Rickard Helldin5fb5df02014-02-07 09:35:04 +01001542 * Count down, since nothing will be done. The observer will be
1543 * notified when we are done so shutdown sequence can continue.
Mike Lockwood7fa24aa2011-03-23 14:52:34 -04001544 */
Rickard Helldin5fb5df02014-02-07 09:35:04 +01001545 mountShutdownLatch.countDown();
1546 Slog.i(TAG, "Unmount completed: " + path +
1547 ", result code: " + StorageResultCode.OperationSucceeded);
Mike Lockwood7fa24aa2011-03-23 14:52:34 -04001548 }
Johan Alfven5d0db4d2010-11-09 10:32:25 +01001549 }
San Mehat4270e1e2010-01-29 05:32:19 -08001550 }
1551 }
1552
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001553 private boolean getUmsEnabling() {
1554 synchronized (mListeners) {
1555 return mUmsEnabling;
1556 }
1557 }
1558
1559 private void setUmsEnabling(boolean enable) {
1560 synchronized (mListeners) {
Tony Wufc711252010-08-09 16:49:19 +08001561 mUmsEnabling = enable;
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001562 }
1563 }
1564
San Mehatb1043402010-02-05 08:26:50 -08001565 public boolean isUsbMassStorageConnected() {
San Mehat207e5382010-02-04 20:46:54 -08001566 waitForReady();
San Mehat91c77612010-01-07 10:39:41 -08001567
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001568 if (getUmsEnabling()) {
San Mehatb1043402010-02-05 08:26:50 -08001569 return true;
San Mehat7fd0fee2009-12-17 07:12:23 -08001570 }
Mike Lockwoodecedfdc2011-06-08 15:11:59 -07001571 synchronized (mListeners) {
1572 return mUmsAvailable;
1573 }
San Mehatb1043402010-02-05 08:26:50 -08001574 }
1575
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001576 public void setUsbMassStorageEnabled(boolean enable) {
San Mehatb1043402010-02-05 08:26:50 -08001577 waitForReady();
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001578 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
Emily Bernier92aa5a22014-07-07 10:11:48 -04001579 validateUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER);
San Mehatb1043402010-02-05 08:26:50 -08001580
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001581 final StorageVolume primary = getPrimaryPhysicalVolume();
1582 if (primary == null) return;
1583
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001584 // TODO: Add support for multiple share methods
1585
1586 /*
1587 * If the volume is mounted and we're enabling then unmount it
1588 */
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001589 String path = primary.getPath();
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001590 String vs = getVolumeState(path);
1591 String method = "ums";
1592 if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
1593 // Override for isUsbMassStorageEnabled()
1594 setUmsEnabling(enable);
1595 UmsEnableCallBack umscb = new UmsEnableCallBack(path, method, true);
1596 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, umscb));
1597 // Clear override
1598 setUmsEnabling(false);
1599 }
1600 /*
1601 * If we disabled UMS then mount the volume
1602 */
1603 if (!enable) {
1604 doShareUnshareVolume(path, method, enable);
1605 if (doMountVolume(path) != StorageResultCode.OperationSucceeded) {
San Mehata5078592010-03-25 09:36:54 -07001606 Slog.e(TAG, "Failed to remount " + path +
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001607 " after disabling share method " + method);
1608 /*
1609 * Even though the mount failed, the unshare didn't so don't indicate an error.
1610 * The mountVolume() call will have set the storage state and sent the necessary
1611 * broadcasts.
1612 */
1613 }
1614 }
San Mehatb1043402010-02-05 08:26:50 -08001615 }
1616
1617 public boolean isUsbMassStorageEnabled() {
1618 waitForReady();
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001619
1620 final StorageVolume primary = getPrimaryPhysicalVolume();
1621 if (primary != null) {
1622 return doGetVolumeShared(primary.getPath(), "ums");
1623 } else {
1624 return false;
1625 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001626 }
Jason parks9ed98bc2011-01-17 09:58:35 -06001627
San Mehat7fd0fee2009-12-17 07:12:23 -08001628 /**
1629 * @return state of the volume at the specified mount point
1630 */
San Mehat4270e1e2010-01-29 05:32:19 -08001631 public String getVolumeState(String mountPoint) {
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001632 synchronized (mVolumesLock) {
Mike Lockwood7fa24aa2011-03-23 14:52:34 -04001633 String state = mVolumeStates.get(mountPoint);
1634 if (state == null) {
1635 Slog.w(TAG, "getVolumeState(" + mountPoint + "): Unknown volume");
Ken Sumrall18db5c52011-07-14 11:35:06 -07001636 if (SystemProperties.get("vold.encrypt_progress").length() != 0) {
1637 state = Environment.MEDIA_REMOVED;
1638 } else {
1639 throw new IllegalArgumentException();
1640 }
Mike Lockwood7fa24aa2011-03-23 14:52:34 -04001641 }
San Mehat7fd0fee2009-12-17 07:12:23 -08001642
Mike Lockwood7fa24aa2011-03-23 14:52:34 -04001643 return state;
1644 }
San Mehat7fd0fee2009-12-17 07:12:23 -08001645 }
1646
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001647 @Override
Kenny Roote1ff2142010-10-12 11:20:01 -07001648 public boolean isExternalStorageEmulated() {
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001649 return mEmulatedTemplate != null;
Kenny Roote1ff2142010-10-12 11:20:01 -07001650 }
1651
San Mehat4270e1e2010-01-29 05:32:19 -08001652 public int mountVolume(String path) {
1653 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
San Mehat207e5382010-02-04 20:46:54 -08001654 waitForReady();
1655 return doMountVolume(path);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001656 }
1657
Ben Komalo13c71972011-09-07 16:35:56 -07001658 public void unmountVolume(String path, boolean force, boolean removeEncryption) {
San Mehat4270e1e2010-01-29 05:32:19 -08001659 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
San Mehat207e5382010-02-04 20:46:54 -08001660 waitForReady();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001661
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -08001662 String volState = getVolumeState(path);
Ben Komalo13c71972011-09-07 16:35:56 -07001663 if (DEBUG_UNMOUNT) {
1664 Slog.i(TAG, "Unmounting " + path
1665 + " force = " + force
1666 + " removeEncryption = " + removeEncryption);
1667 }
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -08001668 if (Environment.MEDIA_UNMOUNTED.equals(volState) ||
1669 Environment.MEDIA_REMOVED.equals(volState) ||
1670 Environment.MEDIA_SHARED.equals(volState) ||
1671 Environment.MEDIA_UNMOUNTABLE.equals(volState)) {
1672 // Media already unmounted or cannot be unmounted.
1673 // TODO return valid return code when adding observer call back.
1674 return;
1675 }
Ben Komalo13c71972011-09-07 16:35:56 -07001676 UnmountCallBack ucb = new UnmountCallBack(path, force, removeEncryption);
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -08001677 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001678 }
1679
San Mehat4270e1e2010-01-29 05:32:19 -08001680 public int formatVolume(String path) {
1681 validatePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
San Mehat207e5382010-02-04 20:46:54 -08001682 waitForReady();
San Mehat5b77dab2010-01-26 13:28:50 -08001683
San Mehat207e5382010-02-04 20:46:54 -08001684 return doFormatVolume(path);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001685 }
1686
Mike Lockwoodecedfdc2011-06-08 15:11:59 -07001687 public int[] getStorageUsers(String path) {
San Mehatc1b4ce92010-02-16 17:13:03 -08001688 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1689 waitForReady();
1690 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001691 final String[] r = NativeDaemonEvent.filterMessageList(
1692 mConnector.executeForList("storage", "users", path),
1693 VoldResponseCode.StorageUsersListResult);
1694
San Mehatc1b4ce92010-02-16 17:13:03 -08001695 // FMT: <pid> <process name>
1696 int[] data = new int[r.length];
1697 for (int i = 0; i < r.length; i++) {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001698 String[] tok = r[i].split(" ");
San Mehatc1b4ce92010-02-16 17:13:03 -08001699 try {
1700 data[i] = Integer.parseInt(tok[0]);
1701 } catch (NumberFormatException nfe) {
San Mehata5078592010-03-25 09:36:54 -07001702 Slog.e(TAG, String.format("Error parsing pid %s", tok[0]));
San Mehatc1b4ce92010-02-16 17:13:03 -08001703 return new int[0];
1704 }
1705 }
1706 return data;
1707 } catch (NativeDaemonConnectorException e) {
San Mehata5078592010-03-25 09:36:54 -07001708 Slog.e(TAG, "Failed to retrieve storage users list", e);
San Mehatc1b4ce92010-02-16 17:13:03 -08001709 return new int[0];
1710 }
1711 }
1712
San Mehatb1043402010-02-05 08:26:50 -08001713 private void warnOnNotMounted() {
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001714 final StorageVolume primary = getPrimaryPhysicalVolume();
Jeff Sharkey32ee8312012-09-30 13:21:31 -07001715 if (primary != null) {
1716 boolean mounted = false;
1717 try {
1718 mounted = Environment.MEDIA_MOUNTED.equals(getVolumeState(primary.getPath()));
Jeff Sharkey9ae62f52013-03-26 10:29:01 -07001719 } catch (IllegalArgumentException e) {
Jeff Sharkey32ee8312012-09-30 13:21:31 -07001720 }
1721
1722 if (!mounted) {
1723 Slog.w(TAG, "getSecureContainerList() called when storage not mounted");
1724 }
San Mehatb1043402010-02-05 08:26:50 -08001725 }
1726 }
1727
San Mehat4270e1e2010-01-29 05:32:19 -08001728 public String[] getSecureContainerList() {
1729 validatePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat207e5382010-02-04 20:46:54 -08001730 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001731 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001732
San Mehat4270e1e2010-01-29 05:32:19 -08001733 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001734 return NativeDaemonEvent.filterMessageList(
1735 mConnector.executeForList("asec", "list"), VoldResponseCode.AsecListResult);
San Mehat4270e1e2010-01-29 05:32:19 -08001736 } catch (NativeDaemonConnectorException e) {
1737 return new String[0];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001738 }
1739 }
San Mehat36972292010-01-06 11:06:32 -08001740
Kenny Root6dceb882012-04-12 14:23:49 -07001741 public int createSecureContainer(String id, int sizeMb, String fstype, String key,
1742 int ownerUid, boolean external) {
San Mehat4270e1e2010-01-29 05:32:19 -08001743 validatePermission(android.Manifest.permission.ASEC_CREATE);
San Mehat207e5382010-02-04 20:46:54 -08001744 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001745 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001746
San Mehatb1043402010-02-05 08:26:50 -08001747 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001748 try {
Jeff Sharkey56cd6462013-06-07 15:09:15 -07001749 mConnector.execute("asec", "create", id, sizeMb, fstype, new SensitiveArg(key),
1750 ownerUid, external ? "1" : "0");
San Mehat4270e1e2010-01-29 05:32:19 -08001751 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001752 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001753 }
San Mehata181b212010-02-11 06:50:20 -08001754
1755 if (rc == StorageResultCode.OperationSucceeded) {
1756 synchronized (mAsecMountSet) {
1757 mAsecMountSet.add(id);
1758 }
1759 }
San Mehat4270e1e2010-01-29 05:32:19 -08001760 return rc;
San Mehat36972292010-01-06 11:06:32 -08001761 }
1762
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001763 @Override
1764 public int resizeSecureContainer(String id, int sizeMb, String key) {
1765 validatePermission(android.Manifest.permission.ASEC_CREATE);
1766 waitForReady();
1767 warnOnNotMounted();
1768
1769 int rc = StorageResultCode.OperationSucceeded;
1770 try {
1771 mConnector.execute("asec", "resize", id, sizeMb, new SensitiveArg(key));
1772 } catch (NativeDaemonConnectorException e) {
1773 rc = StorageResultCode.OperationFailedInternalError;
1774 }
1775 return rc;
1776 }
1777
San Mehat4270e1e2010-01-29 05:32:19 -08001778 public int finalizeSecureContainer(String id) {
1779 validatePermission(android.Manifest.permission.ASEC_CREATE);
San Mehatb1043402010-02-05 08:26:50 -08001780 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001781
San Mehatb1043402010-02-05 08:26:50 -08001782 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001783 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001784 mConnector.execute("asec", "finalize", id);
San Mehata181b212010-02-11 06:50:20 -08001785 /*
1786 * Finalization does a remount, so no need
1787 * to update mAsecMountSet
1788 */
San Mehat4270e1e2010-01-29 05:32:19 -08001789 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001790 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001791 }
San Mehat4270e1e2010-01-29 05:32:19 -08001792 return rc;
San Mehat36972292010-01-06 11:06:32 -08001793 }
1794
Kenny Root6dceb882012-04-12 14:23:49 -07001795 public int fixPermissionsSecureContainer(String id, int gid, String filename) {
1796 validatePermission(android.Manifest.permission.ASEC_CREATE);
1797 warnOnNotMounted();
1798
1799 int rc = StorageResultCode.OperationSucceeded;
1800 try {
1801 mConnector.execute("asec", "fixperms", id, gid, filename);
1802 /*
1803 * Fix permissions does a remount, so no need to update
1804 * mAsecMountSet
1805 */
1806 } catch (NativeDaemonConnectorException e) {
1807 rc = StorageResultCode.OperationFailedInternalError;
1808 }
1809 return rc;
1810 }
1811
San Mehatd9709982010-02-18 11:43:03 -08001812 public int destroySecureContainer(String id, boolean force) {
San Mehat4270e1e2010-01-29 05:32:19 -08001813 validatePermission(android.Manifest.permission.ASEC_DESTROY);
San Mehat207e5382010-02-04 20:46:54 -08001814 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001815 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001816
Kenny Rootaa485402010-09-14 14:49:41 -07001817 /*
1818 * Force a GC to make sure AssetManagers in other threads of the
1819 * system_server are cleaned up. We have to do this since AssetManager
1820 * instances are kept as a WeakReference and it's possible we have files
1821 * open on the external storage.
1822 */
1823 Runtime.getRuntime().gc();
1824
San Mehatb1043402010-02-05 08:26:50 -08001825 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001826 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001827 final Command cmd = new Command("asec", "destroy", id);
1828 if (force) {
1829 cmd.appendArg("force");
1830 }
1831 mConnector.execute(cmd);
San Mehat4270e1e2010-01-29 05:32:19 -08001832 } catch (NativeDaemonConnectorException e) {
San Mehatd9709982010-02-18 11:43:03 -08001833 int code = e.getCode();
1834 if (code == VoldResponseCode.OpFailedStorageBusy) {
1835 rc = StorageResultCode.OperationFailedStorageBusy;
1836 } else {
1837 rc = StorageResultCode.OperationFailedInternalError;
1838 }
San Mehat02735bc2010-01-26 15:18:08 -08001839 }
San Mehata181b212010-02-11 06:50:20 -08001840
1841 if (rc == StorageResultCode.OperationSucceeded) {
1842 synchronized (mAsecMountSet) {
1843 if (mAsecMountSet.contains(id)) {
1844 mAsecMountSet.remove(id);
1845 }
1846 }
1847 }
1848
San Mehat4270e1e2010-01-29 05:32:19 -08001849 return rc;
San Mehat36972292010-01-06 11:06:32 -08001850 }
Jason parks9ed98bc2011-01-17 09:58:35 -06001851
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001852 public int mountSecureContainer(String id, String key, int ownerUid, boolean readOnly) {
San Mehat4270e1e2010-01-29 05:32:19 -08001853 validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
San Mehat207e5382010-02-04 20:46:54 -08001854 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001855 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001856
San Mehata181b212010-02-11 06:50:20 -08001857 synchronized (mAsecMountSet) {
1858 if (mAsecMountSet.contains(id)) {
1859 return StorageResultCode.OperationFailedStorageMounted;
1860 }
1861 }
1862
San Mehatb1043402010-02-05 08:26:50 -08001863 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001864 try {
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001865 mConnector.execute("asec", "mount", id, new SensitiveArg(key), ownerUid,
1866 readOnly ? "ro" : "rw");
San Mehat4270e1e2010-01-29 05:32:19 -08001867 } catch (NativeDaemonConnectorException e) {
Kenny Rootf0304622010-03-19 19:20:42 -07001868 int code = e.getCode();
1869 if (code != VoldResponseCode.OpFailedStorageBusy) {
1870 rc = StorageResultCode.OperationFailedInternalError;
1871 }
San Mehat02735bc2010-01-26 15:18:08 -08001872 }
San Mehat6cdd9c02010-02-09 14:45:20 -08001873
1874 if (rc == StorageResultCode.OperationSucceeded) {
1875 synchronized (mAsecMountSet) {
1876 mAsecMountSet.add(id);
1877 }
1878 }
San Mehat4270e1e2010-01-29 05:32:19 -08001879 return rc;
San Mehat36972292010-01-06 11:06:32 -08001880 }
1881
San Mehatd9709982010-02-18 11:43:03 -08001882 public int unmountSecureContainer(String id, boolean force) {
San Mehat4270e1e2010-01-29 05:32:19 -08001883 validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
San Mehat207e5382010-02-04 20:46:54 -08001884 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001885 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001886
San Mehat6cdd9c02010-02-09 14:45:20 -08001887 synchronized (mAsecMountSet) {
1888 if (!mAsecMountSet.contains(id)) {
San Mehata181b212010-02-11 06:50:20 -08001889 return StorageResultCode.OperationFailedStorageNotMounted;
San Mehat6cdd9c02010-02-09 14:45:20 -08001890 }
1891 }
1892
Kenny Rootaa485402010-09-14 14:49:41 -07001893 /*
1894 * Force a GC to make sure AssetManagers in other threads of the
1895 * system_server are cleaned up. We have to do this since AssetManager
1896 * instances are kept as a WeakReference and it's possible we have files
1897 * open on the external storage.
1898 */
1899 Runtime.getRuntime().gc();
1900
San Mehatb1043402010-02-05 08:26:50 -08001901 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001902 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001903 final Command cmd = new Command("asec", "unmount", id);
1904 if (force) {
1905 cmd.appendArg("force");
1906 }
1907 mConnector.execute(cmd);
San Mehat4270e1e2010-01-29 05:32:19 -08001908 } catch (NativeDaemonConnectorException e) {
San Mehatd9709982010-02-18 11:43:03 -08001909 int code = e.getCode();
1910 if (code == VoldResponseCode.OpFailedStorageBusy) {
1911 rc = StorageResultCode.OperationFailedStorageBusy;
1912 } else {
1913 rc = StorageResultCode.OperationFailedInternalError;
1914 }
San Mehat02735bc2010-01-26 15:18:08 -08001915 }
San Mehat6cdd9c02010-02-09 14:45:20 -08001916
1917 if (rc == StorageResultCode.OperationSucceeded) {
1918 synchronized (mAsecMountSet) {
1919 mAsecMountSet.remove(id);
1920 }
1921 }
San Mehat4270e1e2010-01-29 05:32:19 -08001922 return rc;
San Mehat9dba7092010-01-18 06:47:41 -08001923 }
1924
San Mehat6cdd9c02010-02-09 14:45:20 -08001925 public boolean isSecureContainerMounted(String id) {
1926 validatePermission(android.Manifest.permission.ASEC_ACCESS);
1927 waitForReady();
1928 warnOnNotMounted();
1929
1930 synchronized (mAsecMountSet) {
1931 return mAsecMountSet.contains(id);
1932 }
1933 }
1934
San Mehat4270e1e2010-01-29 05:32:19 -08001935 public int renameSecureContainer(String oldId, String newId) {
1936 validatePermission(android.Manifest.permission.ASEC_RENAME);
San Mehat207e5382010-02-04 20:46:54 -08001937 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001938 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001939
San Mehata181b212010-02-11 06:50:20 -08001940 synchronized (mAsecMountSet) {
San Mehat85451ee2010-02-24 08:54:18 -08001941 /*
Jason parks9ed98bc2011-01-17 09:58:35 -06001942 * Because a mounted container has active internal state which cannot be
San Mehat85451ee2010-02-24 08:54:18 -08001943 * changed while active, we must ensure both ids are not currently mounted.
1944 */
1945 if (mAsecMountSet.contains(oldId) || mAsecMountSet.contains(newId)) {
San Mehata181b212010-02-11 06:50:20 -08001946 return StorageResultCode.OperationFailedStorageMounted;
1947 }
1948 }
1949
San Mehatb1043402010-02-05 08:26:50 -08001950 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001951 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001952 mConnector.execute("asec", "rename", oldId, newId);
San Mehat4270e1e2010-01-29 05:32:19 -08001953 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001954 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001955 }
San Mehata181b212010-02-11 06:50:20 -08001956
San Mehat4270e1e2010-01-29 05:32:19 -08001957 return rc;
San Mehat45f61042010-01-23 08:12:43 -08001958 }
1959
San Mehat4270e1e2010-01-29 05:32:19 -08001960 public String getSecureContainerPath(String id) {
1961 validatePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat207e5382010-02-04 20:46:54 -08001962 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001963 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001964
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001965 final NativeDaemonEvent event;
San Mehat2d66cef2010-03-23 11:12:52 -07001966 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001967 event = mConnector.execute("asec", "path", id);
1968 event.checkCode(VoldResponseCode.AsecPathResult);
1969 return event.getMessage();
San Mehat2d66cef2010-03-23 11:12:52 -07001970 } catch (NativeDaemonConnectorException e) {
1971 int code = e.getCode();
1972 if (code == VoldResponseCode.OpFailedStorageNotFound) {
Fredrik Helmera20c8ef2011-02-09 16:16:10 +01001973 Slog.i(TAG, String.format("Container '%s' not found", id));
1974 return null;
San Mehat22dd86e2010-01-12 12:21:18 -08001975 } else {
San Mehat2d66cef2010-03-23 11:12:52 -07001976 throw new IllegalStateException(String.format("Unexpected response code %d", code));
San Mehat22dd86e2010-01-12 12:21:18 -08001977 }
1978 }
San Mehat22dd86e2010-01-12 12:21:18 -08001979 }
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -07001980
Dianne Hackborn292f8bc2011-06-27 16:27:41 -07001981 public String getSecureContainerFilesystemPath(String id) {
1982 validatePermission(android.Manifest.permission.ASEC_ACCESS);
1983 waitForReady();
1984 warnOnNotMounted();
1985
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001986 final NativeDaemonEvent event;
Dianne Hackborn292f8bc2011-06-27 16:27:41 -07001987 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001988 event = mConnector.execute("asec", "fspath", id);
1989 event.checkCode(VoldResponseCode.AsecPathResult);
1990 return event.getMessage();
Dianne Hackborn292f8bc2011-06-27 16:27:41 -07001991 } catch (NativeDaemonConnectorException e) {
1992 int code = e.getCode();
1993 if (code == VoldResponseCode.OpFailedStorageNotFound) {
1994 Slog.i(TAG, String.format("Container '%s' not found", id));
1995 return null;
1996 } else {
1997 throw new IllegalStateException(String.format("Unexpected response code %d", code));
1998 }
1999 }
2000 }
2001
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -07002002 public void finishMediaUpdate() {
2003 mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
2004 }
Kenny Root02c87302010-07-01 08:10:18 -07002005
Kenny Roota02b8b02010-08-05 16:14:17 -07002006 private boolean isUidOwnerOfPackageOrSystem(String packageName, int callerUid) {
2007 if (callerUid == android.os.Process.SYSTEM_UID) {
2008 return true;
2009 }
2010
Kenny Root02c87302010-07-01 08:10:18 -07002011 if (packageName == null) {
2012 return false;
2013 }
2014
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07002015 final int packageUid = mPms.getPackageUid(packageName, UserHandle.getUserId(callerUid));
Kenny Root02c87302010-07-01 08:10:18 -07002016
2017 if (DEBUG_OBB) {
2018 Slog.d(TAG, "packageName = " + packageName + ", packageUid = " +
2019 packageUid + ", callerUid = " + callerUid);
2020 }
2021
2022 return callerUid == packageUid;
2023 }
2024
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002025 public String getMountedObbPath(String rawPath) {
2026 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
Kenny Rootaf9d6672010-10-08 09:21:39 -07002027
Kenny Root02c87302010-07-01 08:10:18 -07002028 waitForReady();
2029 warnOnNotMounted();
2030
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002031 final ObbState state;
2032 synchronized (mObbPathToStateMap) {
2033 state = mObbPathToStateMap.get(rawPath);
2034 }
2035 if (state == null) {
2036 Slog.w(TAG, "Failed to find OBB mounted at " + rawPath);
2037 return null;
2038 }
2039
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002040 final NativeDaemonEvent event;
Kenny Root02c87302010-07-01 08:10:18 -07002041 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002042 event = mConnector.execute("obb", "path", state.voldPath);
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002043 event.checkCode(VoldResponseCode.AsecPathResult);
2044 return event.getMessage();
Kenny Root02c87302010-07-01 08:10:18 -07002045 } catch (NativeDaemonConnectorException e) {
2046 int code = e.getCode();
2047 if (code == VoldResponseCode.OpFailedStorageNotFound) {
Kenny Roota02b8b02010-08-05 16:14:17 -07002048 return null;
Kenny Root02c87302010-07-01 08:10:18 -07002049 } else {
2050 throw new IllegalStateException(String.format("Unexpected response code %d", code));
2051 }
2052 }
2053 }
2054
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002055 @Override
2056 public boolean isObbMounted(String rawPath) {
2057 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
Kenny Rootaf9d6672010-10-08 09:21:39 -07002058 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002059 return mObbPathToStateMap.containsKey(rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002060 }
Kenny Root02c87302010-07-01 08:10:18 -07002061 }
2062
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002063 @Override
2064 public void mountObb(
2065 String rawPath, String canonicalPath, String key, IObbActionListener token, int nonce) {
2066 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
2067 Preconditions.checkNotNull(canonicalPath, "canonicalPath cannot be null");
2068 Preconditions.checkNotNull(token, "token cannot be null");
Kenny Rootaf9d6672010-10-08 09:21:39 -07002069
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002070 final int callingUid = Binder.getCallingUid();
2071 final ObbState obbState = new ObbState(rawPath, canonicalPath, callingUid, token, nonce);
2072 final ObbAction action = new MountObbAction(obbState, key, callingUid);
Kenny Roota02b8b02010-08-05 16:14:17 -07002073 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
2074
2075 if (DEBUG_OBB)
2076 Slog.i(TAG, "Send to OBB handler: " + action.toString());
Kenny Root02c87302010-07-01 08:10:18 -07002077 }
2078
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002079 @Override
2080 public void unmountObb(String rawPath, boolean force, IObbActionListener token, int nonce) {
2081 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
2082
2083 final ObbState existingState;
2084 synchronized (mObbPathToStateMap) {
2085 existingState = mObbPathToStateMap.get(rawPath);
Kenny Rootf1121dc2010-09-29 07:30:53 -07002086 }
2087
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002088 if (existingState != null) {
2089 // TODO: separate state object from request data
2090 final int callingUid = Binder.getCallingUid();
2091 final ObbState newState = new ObbState(
2092 rawPath, existingState.canonicalPath, callingUid, token, nonce);
2093 final ObbAction action = new UnmountObbAction(newState, force);
2094 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
Kenny Root02c87302010-07-01 08:10:18 -07002095
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002096 if (DEBUG_OBB)
2097 Slog.i(TAG, "Send to OBB handler: " + action.toString());
2098 } else {
2099 Slog.w(TAG, "Unknown OBB mount at " + rawPath);
2100 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002101 }
2102
Ben Komalo444eca22011-09-01 15:17:44 -07002103 @Override
2104 public int getEncryptionState() {
2105 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2106 "no permission to access the crypt keeper");
2107
2108 waitForReady();
2109
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002110 final NativeDaemonEvent event;
Ben Komalo444eca22011-09-01 15:17:44 -07002111 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002112 event = mConnector.execute("cryptfs", "cryptocomplete");
2113 return Integer.parseInt(event.getMessage());
Ben Komalo444eca22011-09-01 15:17:44 -07002114 } catch (NumberFormatException e) {
2115 // Bad result - unexpected.
2116 Slog.w(TAG, "Unable to parse result from cryptfs cryptocomplete");
2117 return ENCRYPTION_STATE_ERROR_UNKNOWN;
2118 } catch (NativeDaemonConnectorException e) {
2119 // Something bad happened.
2120 Slog.w(TAG, "Error in communicating with cryptfs in validating");
2121 return ENCRYPTION_STATE_ERROR_UNKNOWN;
2122 }
2123 }
2124
Paul Lawrence8e397362014-01-27 15:22:30 -08002125 private String toHex(String password) {
2126 if (password == null) {
Paul Lawrence945490c2014-03-27 16:37:28 +00002127 return new String();
Paul Lawrence8e397362014-01-27 15:22:30 -08002128 }
2129 byte[] bytes = password.getBytes(StandardCharsets.UTF_8);
2130 return new String(Hex.encodeHex(bytes));
2131 }
2132
Paul Lawrence945490c2014-03-27 16:37:28 +00002133 private String fromHex(String hexPassword) {
2134 if (hexPassword == null) {
2135 return null;
2136 }
2137
2138 try {
2139 byte[] bytes = Hex.decodeHex(hexPassword.toCharArray());
2140 return new String(bytes, StandardCharsets.UTF_8);
2141 } catch (DecoderException e) {
2142 return null;
2143 }
2144 }
2145
Ben Komalo444eca22011-09-01 15:17:44 -07002146 @Override
Jason parks5af0b912010-11-29 09:05:25 -06002147 public int decryptStorage(String password) {
Jason parksf7b3cd42011-01-27 09:28:25 -06002148 if (TextUtils.isEmpty(password)) {
2149 throw new IllegalArgumentException("password cannot be empty");
Jason parks5af0b912010-11-29 09:05:25 -06002150 }
2151
Jason parks8888c592011-01-20 22:46:41 -06002152 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2153 "no permission to access the crypt keeper");
Jason parks5af0b912010-11-29 09:05:25 -06002154
2155 waitForReady();
2156
2157 if (DEBUG_EVENTS) {
2158 Slog.i(TAG, "decrypting storage...");
2159 }
2160
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002161 final NativeDaemonEvent event;
Jason parks5af0b912010-11-29 09:05:25 -06002162 try {
Paul Lawrence8e397362014-01-27 15:22:30 -08002163 event = mConnector.execute("cryptfs", "checkpw", new SensitiveArg(toHex(password)));
Jason parks9ed98bc2011-01-17 09:58:35 -06002164
Fredrik Roubertda6aedf2011-12-20 17:34:43 +01002165 final int code = Integer.parseInt(event.getMessage());
Jason parks9ed98bc2011-01-17 09:58:35 -06002166 if (code == 0) {
2167 // Decrypt was successful. Post a delayed message before restarting in order
2168 // to let the UI to clear itself
2169 mHandler.postDelayed(new Runnable() {
2170 public void run() {
Jeff Sharkey31c6e482011-11-18 17:09:01 -08002171 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002172 mConnector.execute("cryptfs", "restart");
Jeff Sharkey31c6e482011-11-18 17:09:01 -08002173 } catch (NativeDaemonConnectorException e) {
2174 Slog.e(TAG, "problem executing in background", e);
2175 }
Jason parks9ed98bc2011-01-17 09:58:35 -06002176 }
Jason parksf7b3cd42011-01-27 09:28:25 -06002177 }, 1000); // 1 second
Jason parks9ed98bc2011-01-17 09:58:35 -06002178 }
2179
2180 return code;
Jason parks5af0b912010-11-29 09:05:25 -06002181 } catch (NativeDaemonConnectorException e) {
2182 // Decryption failed
2183 return e.getCode();
2184 }
Jason parks5af0b912010-11-29 09:05:25 -06002185 }
2186
Paul Lawrence46791e72014-04-03 09:10:26 -07002187 public int encryptStorage(int type, String password) {
2188 if (TextUtils.isEmpty(password) && type != StorageManager.CRYPT_TYPE_DEFAULT) {
Jason parksf7b3cd42011-01-27 09:28:25 -06002189 throw new IllegalArgumentException("password cannot be empty");
Jason parks56aa5322011-01-07 09:01:15 -06002190 }
2191
Jason parks8888c592011-01-20 22:46:41 -06002192 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2193 "no permission to access the crypt keeper");
Jason parks56aa5322011-01-07 09:01:15 -06002194
2195 waitForReady();
2196
2197 if (DEBUG_EVENTS) {
Jason parks8888c592011-01-20 22:46:41 -06002198 Slog.i(TAG, "encrypting storage...");
Jason parks56aa5322011-01-07 09:01:15 -06002199 }
2200
2201 try {
Paul Lawrence46791e72014-04-03 09:10:26 -07002202 mConnector.execute("cryptfs", "enablecrypto", "inplace", CRYPTO_TYPES[type],
Paul Lawrence8e397362014-01-27 15:22:30 -08002203 new SensitiveArg(toHex(password)));
Jason parks56aa5322011-01-07 09:01:15 -06002204 } catch (NativeDaemonConnectorException e) {
2205 // Encryption failed
2206 return e.getCode();
2207 }
2208
2209 return 0;
2210 }
2211
Paul Lawrence8e397362014-01-27 15:22:30 -08002212 /** Set the password for encrypting the master key.
2213 * @param type One of the CRYPTO_TYPE_XXX consts defined in StorageManager.
2214 * @param password The password to set.
2215 */
2216 public int changeEncryptionPassword(int type, String password) {
Jason parksf7b3cd42011-01-27 09:28:25 -06002217 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2218 "no permission to access the crypt keeper");
2219
2220 waitForReady();
2221
2222 if (DEBUG_EVENTS) {
2223 Slog.i(TAG, "changing encryption password...");
2224 }
2225
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002226 final NativeDaemonEvent event;
Jason parksf7b3cd42011-01-27 09:28:25 -06002227 try {
Paul Lawrence8e397362014-01-27 15:22:30 -08002228 event = mConnector.execute("cryptfs", "changepw", CRYPTO_TYPES[type],
2229 new SensitiveArg(toHex(password)));
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002230 return Integer.parseInt(event.getMessage());
Jason parksf7b3cd42011-01-27 09:28:25 -06002231 } catch (NativeDaemonConnectorException e) {
2232 // Encryption failed
2233 return e.getCode();
2234 }
2235 }
2236
Christopher Tate32418be2011-10-10 13:51:12 -07002237 /**
2238 * Validate a user-supplied password string with cryptfs
2239 */
2240 @Override
2241 public int verifyEncryptionPassword(String password) throws RemoteException {
2242 // Only the system process is permitted to validate passwords
2243 if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
2244 throw new SecurityException("no permission to access the crypt keeper");
2245 }
2246
2247 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2248 "no permission to access the crypt keeper");
2249
2250 if (TextUtils.isEmpty(password)) {
2251 throw new IllegalArgumentException("password cannot be empty");
2252 }
2253
2254 waitForReady();
2255
2256 if (DEBUG_EVENTS) {
2257 Slog.i(TAG, "validating encryption password...");
2258 }
2259
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002260 final NativeDaemonEvent event;
Christopher Tate32418be2011-10-10 13:51:12 -07002261 try {
Paul Lawrence8e397362014-01-27 15:22:30 -08002262 event = mConnector.execute("cryptfs", "verifypw", new SensitiveArg(toHex(password)));
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002263 Slog.i(TAG, "cryptfs verifypw => " + event.getMessage());
2264 return Integer.parseInt(event.getMessage());
Christopher Tate32418be2011-10-10 13:51:12 -07002265 } catch (NativeDaemonConnectorException e) {
2266 // Encryption failed
2267 return e.getCode();
2268 }
2269 }
2270
Paul Lawrence8e397362014-01-27 15:22:30 -08002271 /**
2272 * Get the type of encryption used to encrypt the master key.
2273 * @return The type, one of the CRYPT_TYPE_XXX consts from StorageManager.
2274 */
2275 @Override
2276 public int getPasswordType() throws RemoteException {
2277
2278 waitForReady();
2279
2280 final NativeDaemonEvent event;
2281 try {
2282 event = mConnector.execute("cryptfs", "getpwtype");
2283 for (int i = 0; i < CRYPTO_TYPES.length; ++i) {
2284 if (CRYPTO_TYPES[i].equals(event.getMessage()))
2285 return i;
2286 }
2287
2288 throw new IllegalStateException("unexpected return from cryptfs");
2289 } catch (NativeDaemonConnectorException e) {
2290 throw e.rethrowAsParcelableException();
2291 }
2292 }
2293
Paul Lawrencee51dcf92014-03-18 10:56:00 -07002294 /**
2295 * Set a field in the crypto header.
2296 * @param field field to set
2297 * @param contents contents to set in field
2298 */
2299 @Override
2300 public void setField(String field, String contents) throws RemoteException {
2301
2302 waitForReady();
2303
2304 final NativeDaemonEvent event;
2305 try {
2306 event = mConnector.execute("cryptfs", "setfield", field, contents);
2307 } catch (NativeDaemonConnectorException e) {
2308 throw e.rethrowAsParcelableException();
2309 }
2310 }
2311
2312 /**
2313 * Gets a field from the crypto header.
2314 * @param field field to get
2315 * @return contents of field
2316 */
2317 @Override
2318 public String getField(String field) throws RemoteException {
2319
2320 waitForReady();
2321
2322 final NativeDaemonEvent event;
2323 try {
2324 final String[] contents = NativeDaemonEvent.filterMessageList(
2325 mConnector.executeForList("cryptfs", "getfield", field),
2326 VoldResponseCode.CryptfsGetfieldResult);
2327 String result = new String();
2328 for (String content : contents) {
2329 result += content;
2330 }
2331 return result;
2332 } catch (NativeDaemonConnectorException e) {
2333 throw e.rethrowAsParcelableException();
2334 }
2335 }
2336
Jeff Sharkeyb049e212012-09-07 23:16:01 -07002337 @Override
Paul Lawrence945490c2014-03-27 16:37:28 +00002338 public String getPassword() throws RemoteException {
2339 if (!isReady()) {
2340 return new String();
2341 }
2342
2343 final NativeDaemonEvent event;
2344 try {
2345 event = mConnector.execute("cryptfs", "getpw");
2346 return fromHex(event.getMessage());
2347 } catch (NativeDaemonConnectorException e) {
2348 throw e.rethrowAsParcelableException();
2349 }
2350 }
2351
2352 @Override
2353 public void clearPassword() throws RemoteException {
2354 if (!isReady()) {
2355 return;
2356 }
2357
2358 final NativeDaemonEvent event;
2359 try {
2360 event = mConnector.execute("cryptfs", "clearpw");
2361 } catch (NativeDaemonConnectorException e) {
2362 throw e.rethrowAsParcelableException();
2363 }
2364 }
2365
2366 @Override
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002367 public int mkdirs(String callingPkg, String appPath) {
2368 final int userId = UserHandle.getUserId(Binder.getCallingUid());
2369 final UserEnvironment userEnv = new UserEnvironment(userId);
2370
2371 // Validate that reported package name belongs to caller
2372 final AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(
2373 Context.APP_OPS_SERVICE);
2374 appOps.checkPackage(Binder.getCallingUid(), callingPkg);
2375
2376 try {
2377 appPath = new File(appPath).getCanonicalPath();
2378 } catch (IOException e) {
2379 Slog.e(TAG, "Failed to resolve " + appPath + ": " + e);
2380 return -1;
2381 }
2382
Jeff Sharkey5786a272013-10-02 12:50:34 -07002383 if (!appPath.endsWith("/")) {
2384 appPath = appPath + "/";
2385 }
2386
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002387 // Try translating the app path into a vold path, but require that it
2388 // belong to the calling package.
2389 String voldPath = maybeTranslatePathForVold(appPath,
2390 userEnv.buildExternalStorageAppDataDirs(callingPkg),
2391 userEnv.buildExternalStorageAppDataDirsForVold(callingPkg));
2392 if (voldPath != null) {
2393 try {
2394 mConnector.execute("volume", "mkdirs", voldPath);
2395 return 0;
2396 } catch (NativeDaemonConnectorException e) {
2397 return e.getCode();
2398 }
2399 }
2400
2401 voldPath = maybeTranslatePathForVold(appPath,
2402 userEnv.buildExternalStorageAppObbDirs(callingPkg),
2403 userEnv.buildExternalStorageAppObbDirsForVold(callingPkg));
2404 if (voldPath != null) {
2405 try {
2406 mConnector.execute("volume", "mkdirs", voldPath);
2407 return 0;
2408 } catch (NativeDaemonConnectorException e) {
2409 return e.getCode();
2410 }
2411 }
2412
Jeff Sharkey2ee3c1e2014-05-30 15:38:35 -07002413 voldPath = maybeTranslatePathForVold(appPath,
2414 userEnv.buildExternalStorageAppMediaDirs(callingPkg),
2415 userEnv.buildExternalStorageAppMediaDirsForVold(callingPkg));
2416 if (voldPath != null) {
2417 try {
2418 mConnector.execute("volume", "mkdirs", voldPath);
2419 return 0;
2420 } catch (NativeDaemonConnectorException e) {
2421 return e.getCode();
2422 }
2423 }
2424
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002425 throw new SecurityException("Invalid mkdirs path: " + appPath);
2426 }
2427
2428 /**
2429 * Translate the given path from an app-visible path to a vold-visible path,
2430 * but only if it's under the given whitelisted paths.
2431 *
2432 * @param path a canonicalized app-visible path.
2433 * @param appPaths list of app-visible paths that are allowed.
2434 * @param voldPaths list of vold-visible paths directly corresponding to the
2435 * allowed app-visible paths argument.
2436 * @return a vold-visible path representing the original path, or
2437 * {@code null} if the given path didn't have an app-to-vold
2438 * mapping.
2439 */
2440 @VisibleForTesting
2441 public static String maybeTranslatePathForVold(
2442 String path, File[] appPaths, File[] voldPaths) {
2443 if (appPaths.length != voldPaths.length) {
2444 throw new IllegalStateException("Paths must be 1:1 mapping");
2445 }
2446
2447 for (int i = 0; i < appPaths.length; i++) {
Jeff Sharkey5786a272013-10-02 12:50:34 -07002448 final String appPath = appPaths[i].getAbsolutePath() + "/";
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002449 if (path.startsWith(appPath)) {
Jeff Sharkey5786a272013-10-02 12:50:34 -07002450 path = new File(voldPaths[i], path.substring(appPath.length()))
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002451 .getAbsolutePath();
2452 if (!path.endsWith("/")) {
2453 path = path + "/";
2454 }
2455 return path;
2456 }
2457 }
2458 return null;
2459 }
2460
2461 @Override
Jeff Sharkeyb049e212012-09-07 23:16:01 -07002462 public StorageVolume[] getVolumeList() {
2463 final int callingUserId = UserHandle.getCallingUserId();
2464 final boolean accessAll = (mContext.checkPermission(
2465 android.Manifest.permission.ACCESS_ALL_EXTERNAL_STORAGE,
2466 Binder.getCallingPid(), Binder.getCallingUid()) == PERMISSION_GRANTED);
2467
2468 synchronized (mVolumesLock) {
2469 final ArrayList<StorageVolume> filtered = Lists.newArrayList();
2470 for (StorageVolume volume : mVolumes) {
2471 final UserHandle owner = volume.getOwner();
2472 final boolean ownerMatch = owner == null || owner.getIdentifier() == callingUserId;
2473 if (accessAll || ownerMatch) {
2474 filtered.add(volume);
2475 }
Mike Lockwood8fa5f802011-03-24 08:12:30 -07002476 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -07002477 return filtered.toArray(new StorageVolume[filtered.size()]);
Mike Lockwood8fa5f802011-03-24 08:12:30 -07002478 }
2479 }
2480
Kenny Rootaf9d6672010-10-08 09:21:39 -07002481 private void addObbStateLocked(ObbState obbState) throws RemoteException {
2482 final IBinder binder = obbState.getBinder();
2483 List<ObbState> obbStates = mObbMounts.get(binder);
Kenny Root5919ac62010-10-05 09:49:40 -07002484
Kenny Rootaf9d6672010-10-08 09:21:39 -07002485 if (obbStates == null) {
2486 obbStates = new ArrayList<ObbState>();
2487 mObbMounts.put(binder, obbStates);
2488 } else {
2489 for (final ObbState o : obbStates) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002490 if (o.rawPath.equals(obbState.rawPath)) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002491 throw new IllegalStateException("Attempt to add ObbState twice. "
2492 + "This indicates an error in the MountService logic.");
Kenny Root5919ac62010-10-05 09:49:40 -07002493 }
2494 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002495 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07002496
2497 obbStates.add(obbState);
2498 try {
2499 obbState.link();
2500 } catch (RemoteException e) {
2501 /*
2502 * The binder died before we could link it, so clean up our state
2503 * and return failure.
2504 */
2505 obbStates.remove(obbState);
2506 if (obbStates.isEmpty()) {
2507 mObbMounts.remove(binder);
2508 }
2509
2510 // Rethrow the error so mountObb can get it
2511 throw e;
2512 }
2513
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002514 mObbPathToStateMap.put(obbState.rawPath, obbState);
Kenny Roota02b8b02010-08-05 16:14:17 -07002515 }
2516
Kenny Rootaf9d6672010-10-08 09:21:39 -07002517 private void removeObbStateLocked(ObbState obbState) {
2518 final IBinder binder = obbState.getBinder();
2519 final List<ObbState> obbStates = mObbMounts.get(binder);
2520 if (obbStates != null) {
2521 if (obbStates.remove(obbState)) {
2522 obbState.unlink();
Kenny Root05105f72010-09-22 17:29:43 -07002523 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07002524 if (obbStates.isEmpty()) {
2525 mObbMounts.remove(binder);
2526 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002527 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002528
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002529 mObbPathToStateMap.remove(obbState.rawPath);
Kenny Root38cf8862010-09-26 14:18:51 -07002530 }
2531
Kenny Roota02b8b02010-08-05 16:14:17 -07002532 private class ObbActionHandler extends Handler {
2533 private boolean mBound = false;
Kenny Root480afe72010-10-07 10:17:50 -07002534 private final List<ObbAction> mActions = new LinkedList<ObbAction>();
Kenny Roota02b8b02010-08-05 16:14:17 -07002535
2536 ObbActionHandler(Looper l) {
2537 super(l);
2538 }
2539
2540 @Override
2541 public void handleMessage(Message msg) {
2542 switch (msg.what) {
2543 case OBB_RUN_ACTION: {
Kenny Root480afe72010-10-07 10:17:50 -07002544 final ObbAction action = (ObbAction) msg.obj;
Kenny Roota02b8b02010-08-05 16:14:17 -07002545
2546 if (DEBUG_OBB)
2547 Slog.i(TAG, "OBB_RUN_ACTION: " + action.toString());
2548
2549 // If a bind was already initiated we don't really
2550 // need to do anything. The pending install
2551 // will be processed later on.
2552 if (!mBound) {
2553 // If this is the only one pending we might
2554 // have to bind to the service again.
2555 if (!connectToService()) {
2556 Slog.e(TAG, "Failed to bind to media container service");
2557 action.handleError();
2558 return;
Kenny Roota02b8b02010-08-05 16:14:17 -07002559 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002560 }
Kenny Root735de3b2010-09-30 14:11:39 -07002561
Kenny Root735de3b2010-09-30 14:11:39 -07002562 mActions.add(action);
Kenny Roota02b8b02010-08-05 16:14:17 -07002563 break;
2564 }
2565 case OBB_MCS_BOUND: {
2566 if (DEBUG_OBB)
2567 Slog.i(TAG, "OBB_MCS_BOUND");
2568 if (msg.obj != null) {
2569 mContainerService = (IMediaContainerService) msg.obj;
2570 }
2571 if (mContainerService == null) {
2572 // Something seriously wrong. Bail out
2573 Slog.e(TAG, "Cannot bind to media container service");
2574 for (ObbAction action : mActions) {
2575 // Indicate service bind error
2576 action.handleError();
2577 }
2578 mActions.clear();
2579 } else if (mActions.size() > 0) {
Kenny Root480afe72010-10-07 10:17:50 -07002580 final ObbAction action = mActions.get(0);
Kenny Roota02b8b02010-08-05 16:14:17 -07002581 if (action != null) {
2582 action.execute(this);
2583 }
2584 } else {
2585 // Should never happen ideally.
2586 Slog.w(TAG, "Empty queue");
2587 }
2588 break;
2589 }
2590 case OBB_MCS_RECONNECT: {
2591 if (DEBUG_OBB)
2592 Slog.i(TAG, "OBB_MCS_RECONNECT");
2593 if (mActions.size() > 0) {
2594 if (mBound) {
2595 disconnectService();
2596 }
2597 if (!connectToService()) {
2598 Slog.e(TAG, "Failed to bind to media container service");
2599 for (ObbAction action : mActions) {
2600 // Indicate service bind error
2601 action.handleError();
2602 }
2603 mActions.clear();
2604 }
2605 }
2606 break;
2607 }
2608 case OBB_MCS_UNBIND: {
2609 if (DEBUG_OBB)
2610 Slog.i(TAG, "OBB_MCS_UNBIND");
2611
2612 // Delete pending install
2613 if (mActions.size() > 0) {
2614 mActions.remove(0);
2615 }
2616 if (mActions.size() == 0) {
2617 if (mBound) {
2618 disconnectService();
2619 }
2620 } else {
2621 // There are more pending requests in queue.
2622 // Just post MCS_BOUND message to trigger processing
2623 // of next pending install.
2624 mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND);
2625 }
2626 break;
2627 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07002628 case OBB_FLUSH_MOUNT_STATE: {
2629 final String path = (String) msg.obj;
2630
2631 if (DEBUG_OBB)
2632 Slog.i(TAG, "Flushing all OBB state for path " + path);
2633
2634 synchronized (mObbMounts) {
2635 final List<ObbState> obbStatesToRemove = new LinkedList<ObbState>();
2636
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002637 final Iterator<ObbState> i = mObbPathToStateMap.values().iterator();
Kenny Rootaf9d6672010-10-08 09:21:39 -07002638 while (i.hasNext()) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002639 final ObbState state = i.next();
Kenny Rootaf9d6672010-10-08 09:21:39 -07002640
2641 /*
2642 * If this entry's source file is in the volume path
2643 * that got unmounted, remove it because it's no
2644 * longer valid.
2645 */
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002646 if (state.canonicalPath.startsWith(path)) {
2647 obbStatesToRemove.add(state);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002648 }
2649 }
2650
2651 for (final ObbState obbState : obbStatesToRemove) {
2652 if (DEBUG_OBB)
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002653 Slog.i(TAG, "Removing state for " + obbState.rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002654
2655 removeObbStateLocked(obbState);
2656
2657 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002658 obbState.token.onObbResult(obbState.rawPath, obbState.nonce,
Kenny Rootaf9d6672010-10-08 09:21:39 -07002659 OnObbStateChangeListener.UNMOUNTED);
2660 } catch (RemoteException e) {
2661 Slog.i(TAG, "Couldn't send unmount notification for OBB: "
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002662 + obbState.rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002663 }
2664 }
2665 }
2666 break;
2667 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002668 }
2669 }
2670
2671 private boolean connectToService() {
2672 if (DEBUG_OBB)
2673 Slog.i(TAG, "Trying to bind to DefaultContainerService");
2674
2675 Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
2676 if (mContext.bindService(service, mDefContainerConn, Context.BIND_AUTO_CREATE)) {
2677 mBound = true;
2678 return true;
2679 }
2680 return false;
2681 }
2682
2683 private void disconnectService() {
2684 mContainerService = null;
2685 mBound = false;
2686 mContext.unbindService(mDefContainerConn);
2687 }
2688 }
2689
2690 abstract class ObbAction {
2691 private static final int MAX_RETRIES = 3;
2692 private int mRetries;
2693
2694 ObbState mObbState;
2695
2696 ObbAction(ObbState obbState) {
2697 mObbState = obbState;
2698 }
2699
2700 public void execute(ObbActionHandler handler) {
2701 try {
2702 if (DEBUG_OBB)
Ben Komalo444eca22011-09-01 15:17:44 -07002703 Slog.i(TAG, "Starting to execute action: " + toString());
Kenny Roota02b8b02010-08-05 16:14:17 -07002704 mRetries++;
2705 if (mRetries > MAX_RETRIES) {
2706 Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
Kenny Root480afe72010-10-07 10:17:50 -07002707 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
Kenny Roota02b8b02010-08-05 16:14:17 -07002708 handleError();
2709 return;
2710 } else {
2711 handleExecute();
2712 if (DEBUG_OBB)
2713 Slog.i(TAG, "Posting install MCS_UNBIND");
2714 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
2715 }
2716 } catch (RemoteException e) {
2717 if (DEBUG_OBB)
2718 Slog.i(TAG, "Posting install MCS_RECONNECT");
2719 mObbActionHandler.sendEmptyMessage(OBB_MCS_RECONNECT);
2720 } catch (Exception e) {
2721 if (DEBUG_OBB)
2722 Slog.d(TAG, "Error handling OBB action", e);
2723 handleError();
Kenny Root17eb6fb2010-10-06 15:02:52 -07002724 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
Kenny Roota02b8b02010-08-05 16:14:17 -07002725 }
2726 }
2727
Kenny Root05105f72010-09-22 17:29:43 -07002728 abstract void handleExecute() throws RemoteException, IOException;
Kenny Roota02b8b02010-08-05 16:14:17 -07002729 abstract void handleError();
Kenny Root38cf8862010-09-26 14:18:51 -07002730
2731 protected ObbInfo getObbInfo() throws IOException {
2732 ObbInfo obbInfo;
2733 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002734 obbInfo = mContainerService.getObbInfo(mObbState.ownerPath);
Kenny Root38cf8862010-09-26 14:18:51 -07002735 } catch (RemoteException e) {
2736 Slog.d(TAG, "Couldn't call DefaultContainerService to fetch OBB info for "
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002737 + mObbState.ownerPath);
Kenny Root38cf8862010-09-26 14:18:51 -07002738 obbInfo = null;
2739 }
2740 if (obbInfo == null) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002741 throw new IOException("Couldn't read OBB file: " + mObbState.ownerPath);
Kenny Root38cf8862010-09-26 14:18:51 -07002742 }
2743 return obbInfo;
2744 }
2745
Kenny Rootaf9d6672010-10-08 09:21:39 -07002746 protected void sendNewStatusOrIgnore(int status) {
2747 if (mObbState == null || mObbState.token == null) {
2748 return;
2749 }
2750
Kenny Root38cf8862010-09-26 14:18:51 -07002751 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002752 mObbState.token.onObbResult(mObbState.rawPath, mObbState.nonce, status);
Kenny Root38cf8862010-09-26 14:18:51 -07002753 } catch (RemoteException e) {
2754 Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
2755 }
2756 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002757 }
2758
2759 class MountObbAction extends ObbAction {
Ben Komalo444eca22011-09-01 15:17:44 -07002760 private final String mKey;
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002761 private final int mCallingUid;
Kenny Roota02b8b02010-08-05 16:14:17 -07002762
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002763 MountObbAction(ObbState obbState, String key, int callingUid) {
Kenny Roota02b8b02010-08-05 16:14:17 -07002764 super(obbState);
2765 mKey = key;
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002766 mCallingUid = callingUid;
Kenny Roota02b8b02010-08-05 16:14:17 -07002767 }
2768
Jason parks5af0b912010-11-29 09:05:25 -06002769 @Override
Kenny Root735de3b2010-09-30 14:11:39 -07002770 public void handleExecute() throws IOException, RemoteException {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002771 waitForReady();
2772 warnOnNotMounted();
2773
Kenny Root38cf8862010-09-26 14:18:51 -07002774 final ObbInfo obbInfo = getObbInfo();
2775
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002776 if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mCallingUid)) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002777 Slog.w(TAG, "Denied attempt to mount OBB " + obbInfo.filename
2778 + " which is owned by " + obbInfo.packageName);
2779 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
2780 return;
Kenny Roota02b8b02010-08-05 16:14:17 -07002781 }
2782
Kenny Rootaf9d6672010-10-08 09:21:39 -07002783 final boolean isMounted;
2784 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002785 isMounted = mObbPathToStateMap.containsKey(mObbState.rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002786 }
2787 if (isMounted) {
2788 Slog.w(TAG, "Attempt to mount OBB which is already mounted: " + obbInfo.filename);
2789 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_ALREADY_MOUNTED);
2790 return;
2791 }
2792
Kenny Rootaf9d6672010-10-08 09:21:39 -07002793 final String hashedKey;
2794 if (mKey == null) {
2795 hashedKey = "none";
2796 } else {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002797 try {
Kenny Root3b1abba2010-10-13 15:00:07 -07002798 SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
2799
2800 KeySpec ks = new PBEKeySpec(mKey.toCharArray(), obbInfo.salt,
2801 PBKDF2_HASH_ROUNDS, CRYPTO_ALGORITHM_KEY_SIZE);
2802 SecretKey key = factory.generateSecret(ks);
2803 BigInteger bi = new BigInteger(key.getEncoded());
2804 hashedKey = bi.toString(16);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002805 } catch (NoSuchAlgorithmException e) {
Kenny Root3b1abba2010-10-13 15:00:07 -07002806 Slog.e(TAG, "Could not load PBKDF2 algorithm", e);
2807 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
2808 return;
2809 } catch (InvalidKeySpecException e) {
2810 Slog.e(TAG, "Invalid key spec when loading PBKDF2 algorithm", e);
2811 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
Kenny Root38cf8862010-09-26 14:18:51 -07002812 return;
2813 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07002814 }
Kenny Root38cf8862010-09-26 14:18:51 -07002815
Kenny Rootaf9d6672010-10-08 09:21:39 -07002816 int rc = StorageResultCode.OperationSucceeded;
Kenny Rootaf9d6672010-10-08 09:21:39 -07002817 try {
Jeff Sharkey56cd6462013-06-07 15:09:15 -07002818 mConnector.execute("obb", "mount", mObbState.voldPath, new SensitiveArg(hashedKey),
2819 mObbState.ownerGid);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002820 } catch (NativeDaemonConnectorException e) {
2821 int code = e.getCode();
2822 if (code != VoldResponseCode.OpFailedStorageBusy) {
2823 rc = StorageResultCode.OperationFailedInternalError;
Kenny Roota02b8b02010-08-05 16:14:17 -07002824 }
2825 }
2826
Kenny Rootaf9d6672010-10-08 09:21:39 -07002827 if (rc == StorageResultCode.OperationSucceeded) {
2828 if (DEBUG_OBB)
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002829 Slog.d(TAG, "Successfully mounted OBB " + mObbState.voldPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002830
2831 synchronized (mObbMounts) {
2832 addObbStateLocked(mObbState);
2833 }
2834
2835 sendNewStatusOrIgnore(OnObbStateChangeListener.MOUNTED);
Kenny Root02c87302010-07-01 08:10:18 -07002836 } else {
Kenny Root05105f72010-09-22 17:29:43 -07002837 Slog.e(TAG, "Couldn't mount OBB file: " + rc);
Kenny Roota02b8b02010-08-05 16:14:17 -07002838
Kenny Rootaf9d6672010-10-08 09:21:39 -07002839 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT);
Kenny Root02c87302010-07-01 08:10:18 -07002840 }
2841 }
2842
Jason parks5af0b912010-11-29 09:05:25 -06002843 @Override
Kenny Roota02b8b02010-08-05 16:14:17 -07002844 public void handleError() {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002845 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
Kenny Root02c87302010-07-01 08:10:18 -07002846 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002847
2848 @Override
2849 public String toString() {
2850 StringBuilder sb = new StringBuilder();
2851 sb.append("MountObbAction{");
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002852 sb.append(mObbState);
Kenny Roota02b8b02010-08-05 16:14:17 -07002853 sb.append('}');
2854 return sb.toString();
2855 }
2856 }
2857
2858 class UnmountObbAction extends ObbAction {
Ben Komalo444eca22011-09-01 15:17:44 -07002859 private final boolean mForceUnmount;
Kenny Roota02b8b02010-08-05 16:14:17 -07002860
2861 UnmountObbAction(ObbState obbState, boolean force) {
2862 super(obbState);
2863 mForceUnmount = force;
2864 }
2865
Jason parks5af0b912010-11-29 09:05:25 -06002866 @Override
Kenny Root38cf8862010-09-26 14:18:51 -07002867 public void handleExecute() throws IOException {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002868 waitForReady();
2869 warnOnNotMounted();
2870
Kenny Root38cf8862010-09-26 14:18:51 -07002871 final ObbInfo obbInfo = getObbInfo();
Kenny Roota02b8b02010-08-05 16:14:17 -07002872
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002873 final ObbState existingState;
Kenny Root38cf8862010-09-26 14:18:51 -07002874 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002875 existingState = mObbPathToStateMap.get(mObbState.rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002876 }
Kenny Root38cf8862010-09-26 14:18:51 -07002877
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002878 if (existingState == null) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002879 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_NOT_MOUNTED);
2880 return;
2881 }
2882
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002883 if (existingState.ownerGid != mObbState.ownerGid) {
2884 Slog.w(TAG, "Permission denied attempting to unmount OBB " + existingState.rawPath
2885 + " (owned by GID " + existingState.ownerGid + ")");
Kenny Rootaf9d6672010-10-08 09:21:39 -07002886 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
2887 return;
2888 }
2889
Kenny Rootaf9d6672010-10-08 09:21:39 -07002890 int rc = StorageResultCode.OperationSucceeded;
Kenny Rootaf9d6672010-10-08 09:21:39 -07002891 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002892 final Command cmd = new Command("obb", "unmount", mObbState.voldPath);
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002893 if (mForceUnmount) {
2894 cmd.appendArg("force");
2895 }
2896 mConnector.execute(cmd);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002897 } catch (NativeDaemonConnectorException e) {
2898 int code = e.getCode();
2899 if (code == VoldResponseCode.OpFailedStorageBusy) {
2900 rc = StorageResultCode.OperationFailedStorageBusy;
2901 } else if (code == VoldResponseCode.OpFailedStorageNotFound) {
2902 // If it's not mounted then we've already won.
2903 rc = StorageResultCode.OperationSucceeded;
2904 } else {
2905 rc = StorageResultCode.OperationFailedInternalError;
Kenny Roota02b8b02010-08-05 16:14:17 -07002906 }
2907 }
2908
Kenny Rootaf9d6672010-10-08 09:21:39 -07002909 if (rc == StorageResultCode.OperationSucceeded) {
2910 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002911 removeObbStateLocked(existingState);
Kenny Root38cf8862010-09-26 14:18:51 -07002912 }
2913
Kenny Rootaf9d6672010-10-08 09:21:39 -07002914 sendNewStatusOrIgnore(OnObbStateChangeListener.UNMOUNTED);
Kenny Roota02b8b02010-08-05 16:14:17 -07002915 } else {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002916 Slog.w(TAG, "Could not unmount OBB: " + existingState);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002917 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_UNMOUNT);
Kenny Roota02b8b02010-08-05 16:14:17 -07002918 }
2919 }
2920
Jason parks5af0b912010-11-29 09:05:25 -06002921 @Override
Kenny Roota02b8b02010-08-05 16:14:17 -07002922 public void handleError() {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002923 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
Kenny Roota02b8b02010-08-05 16:14:17 -07002924 }
2925
2926 @Override
2927 public String toString() {
2928 StringBuilder sb = new StringBuilder();
2929 sb.append("UnmountObbAction{");
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002930 sb.append(mObbState);
Kenny Roota02b8b02010-08-05 16:14:17 -07002931 sb.append(",force=");
2932 sb.append(mForceUnmount);
Kenny Roota02b8b02010-08-05 16:14:17 -07002933 sb.append('}');
2934 return sb.toString();
2935 }
Kenny Root02c87302010-07-01 08:10:18 -07002936 }
Kenny Root38cf8862010-09-26 14:18:51 -07002937
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -08002938 @VisibleForTesting
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002939 public static String buildObbPath(final String canonicalPath, int userId, boolean forVold) {
2940 // TODO: allow caller to provide Environment for full testing
Jeff Sharkey1abdb712013-08-11 16:28:14 -07002941 // TODO: extend to support OBB mounts on secondary external storage
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002942
2943 // Only adjust paths when storage is emulated
2944 if (!Environment.isExternalStorageEmulated()) {
2945 return canonicalPath;
2946 }
2947
2948 String path = canonicalPath.toString();
2949
2950 // First trim off any external storage prefix
2951 final UserEnvironment userEnv = new UserEnvironment(userId);
2952
2953 // /storage/emulated/0
Jeff Sharkey1abdb712013-08-11 16:28:14 -07002954 final String externalPath = userEnv.getExternalStorageDirectory().getAbsolutePath();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002955 // /storage/emulated_legacy
2956 final String legacyExternalPath = Environment.getLegacyExternalStorageDirectory()
Jeff Sharkey1abdb712013-08-11 16:28:14 -07002957 .getAbsolutePath();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002958
2959 if (path.startsWith(externalPath)) {
2960 path = path.substring(externalPath.length() + 1);
2961 } else if (path.startsWith(legacyExternalPath)) {
2962 path = path.substring(legacyExternalPath.length() + 1);
2963 } else {
2964 return canonicalPath;
2965 }
2966
2967 // Handle special OBB paths on emulated storage
2968 final String obbPath = "Android/obb";
2969 if (path.startsWith(obbPath)) {
2970 path = path.substring(obbPath.length() + 1);
2971
2972 if (forVold) {
Jeff Sharkey1abdb712013-08-11 16:28:14 -07002973 return new File(Environment.getEmulatedStorageObbSource(), path).getAbsolutePath();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002974 } else {
2975 final UserEnvironment ownerEnv = new UserEnvironment(UserHandle.USER_OWNER);
Jeff Sharkey1abdb712013-08-11 16:28:14 -07002976 return new File(ownerEnv.buildExternalStorageAndroidObbDirs()[0], path)
2977 .getAbsolutePath();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002978 }
2979 }
2980
2981 // Handle normal external storage paths
2982 if (forVold) {
Jeff Sharkey1abdb712013-08-11 16:28:14 -07002983 return new File(Environment.getEmulatedStorageSource(userId), path).getAbsolutePath();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002984 } else {
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002985 return new File(userEnv.getExternalDirsForApp()[0], path).getAbsolutePath();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002986 }
2987 }
2988
Kenny Root38cf8862010-09-26 14:18:51 -07002989 @Override
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002990 protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
2991 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
2992
2993 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ", 160);
Kenny Root38cf8862010-09-26 14:18:51 -07002994
Kenny Root38cf8862010-09-26 14:18:51 -07002995 synchronized (mObbMounts) {
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002996 pw.println("mObbMounts:");
2997 pw.increaseIndent();
2998 final Iterator<Entry<IBinder, List<ObbState>>> binders = mObbMounts.entrySet()
2999 .iterator();
Kenny Rootaf9d6672010-10-08 09:21:39 -07003000 while (binders.hasNext()) {
3001 Entry<IBinder, List<ObbState>> e = binders.next();
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003002 pw.println(e.getKey() + ":");
3003 pw.increaseIndent();
Kenny Rootaf9d6672010-10-08 09:21:39 -07003004 final List<ObbState> obbStates = e.getValue();
Kenny Root38cf8862010-09-26 14:18:51 -07003005 for (final ObbState obbState : obbStates) {
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003006 pw.println(obbState);
Kenny Root38cf8862010-09-26 14:18:51 -07003007 }
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003008 pw.decreaseIndent();
Kenny Root38cf8862010-09-26 14:18:51 -07003009 }
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003010 pw.decreaseIndent();
Kenny Rootaf9d6672010-10-08 09:21:39 -07003011
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003012 pw.println();
3013 pw.println("mObbPathToStateMap:");
3014 pw.increaseIndent();
Kenny Rootaf9d6672010-10-08 09:21:39 -07003015 final Iterator<Entry<String, ObbState>> maps = mObbPathToStateMap.entrySet().iterator();
3016 while (maps.hasNext()) {
3017 final Entry<String, ObbState> e = maps.next();
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003018 pw.print(e.getKey());
3019 pw.print(" -> ");
3020 pw.println(e.getValue());
Kenny Rootaf9d6672010-10-08 09:21:39 -07003021 }
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003022 pw.decreaseIndent();
Kenny Root38cf8862010-09-26 14:18:51 -07003023 }
Kenny Root4161f9b2011-07-13 09:48:33 -07003024
Jeff Sharkeyb049e212012-09-07 23:16:01 -07003025 synchronized (mVolumesLock) {
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003026 pw.println();
3027 pw.println("mVolumes:");
3028 pw.increaseIndent();
3029 for (StorageVolume volume : mVolumes) {
3030 pw.println(volume);
3031 pw.increaseIndent();
3032 pw.println("Current state: " + mVolumeStates.get(volume.getPath()));
3033 pw.decreaseIndent();
Kenny Root4161f9b2011-07-13 09:48:33 -07003034 }
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003035 pw.decreaseIndent();
Kenny Root4161f9b2011-07-13 09:48:33 -07003036 }
Robert Greenwalt470fd722012-01-18 12:51:15 -08003037
3038 pw.println();
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003039 pw.println("mConnection:");
3040 pw.increaseIndent();
Robert Greenwalt470fd722012-01-18 12:51:15 -08003041 mConnector.dump(fd, pw, args);
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003042 pw.decreaseIndent();
Kenny Root38cf8862010-09-26 14:18:51 -07003043 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003044
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -07003045 /** {@inheritDoc} */
3046 public void monitor() {
3047 if (mConnector != null) {
3048 mConnector.monitor();
3049 }
3050 }
3051}