blob: 7f24d07debce7c00e0991c7fa79cebf7508d095e [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;
Elliott Hughesf839b4f2014-09-26 12:30:47 -070022import android.app.ActivityManagerNative;
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -070023import android.app.AppOpsManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.content.BroadcastReceiver;
Kenny Roota02b8b02010-08-05 16:14:17 -070025import android.content.ComponentName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.content.Context;
27import android.content.Intent;
28import android.content.IntentFilter;
Kenny Roota02b8b02010-08-05 16:14:17 -070029import android.content.ServiceConnection;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.content.pm.PackageManager;
Jeff Sharkeyb049e212012-09-07 23:16:01 -070031import android.content.pm.UserInfo;
Elliott Hughesf839b4f2014-09-26 12:30:47 -070032import android.content.res.Configuration;
Kenny Root02c87302010-07-01 08:10:18 -070033import android.content.res.ObbInfo;
Mike Lockwood2f6a3882011-05-09 19:08:06 -070034import android.content.res.Resources;
35import android.content.res.TypedArray;
36import android.content.res.XmlResourceParser;
Mike Lockwoodecedfdc2011-06-08 15:11:59 -070037import android.hardware.usb.UsbManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038import android.net.Uri;
Kenny Root02c87302010-07-01 08:10:18 -070039import android.os.Binder;
Kenny Roota02b8b02010-08-05 16:14:17 -070040import android.os.Environment;
Jeff Sharkeyb049e212012-09-07 23:16:01 -070041import android.os.Environment.UserEnvironment;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080042import android.os.Handler;
Dianne Hackbornefa92b22013-05-03 14:11:43 -070043import android.os.HandlerThread;
Kenny Roota02b8b02010-08-05 16:14:17 -070044import android.os.IBinder;
Daniel Sandler5f27ef42010-03-16 15:42:02 -040045import android.os.Looper;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080046import android.os.Message;
San Mehat4270e1e2010-01-29 05:32:19 -080047import android.os.RemoteException;
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -080048import android.os.ServiceManager;
Svetoslavf23b64d2013-04-25 14:45:54 -070049import android.os.SystemClock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050import android.os.SystemProperties;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070051import android.os.UserHandle;
Emily Bernier92aa5a22014-07-07 10:11:48 -040052import android.os.UserManager;
Kenny Roota02b8b02010-08-05 16:14:17 -070053import android.os.storage.IMountService;
54import android.os.storage.IMountServiceListener;
55import android.os.storage.IMountShutdownObserver;
56import android.os.storage.IObbActionListener;
Kenny Rootaf9d6672010-10-08 09:21:39 -070057import android.os.storage.OnObbStateChangeListener;
Paul Lawrence46791e72014-04-03 09:10:26 -070058import android.os.storage.StorageManager;
Kenny Roota02b8b02010-08-05 16:14:17 -070059import android.os.storage.StorageResultCode;
Mike Lockwood2f6a3882011-05-09 19:08:06 -070060import android.os.storage.StorageVolume;
Jason parksf7b3cd42011-01-27 09:28:25 -060061import android.text.TextUtils;
Mike Lockwood2f6a3882011-05-09 19:08:06 -070062import android.util.AttributeSet;
San Mehata5078592010-03-25 09:36:54 -070063import android.util.Slog;
Mike Lockwood2f6a3882011-05-09 19:08:06 -070064import android.util.Xml;
65
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -080066import com.android.internal.annotations.GuardedBy;
67import com.android.internal.annotations.VisibleForTesting;
Jeff Sharkeyb049e212012-09-07 23:16:01 -070068import com.android.internal.app.IMediaContainerService;
Jeff Sharkey5aca2b82013-10-16 16:21:54 -070069import com.android.internal.util.IndentingPrintWriter;
Jeff Sharkey4fbbda42012-09-24 18:34:07 -070070import com.android.internal.util.Preconditions;
Jeff Sharkeyb049e212012-09-07 23:16:01 -070071import com.android.internal.util.XmlUtils;
72import com.android.server.NativeDaemonConnector.Command;
Jeff Sharkey56cd6462013-06-07 15:09:15 -070073import com.android.server.NativeDaemonConnector.SensitiveArg;
Jeff Sharkeyb049e212012-09-07 23:16:01 -070074import com.android.server.am.ActivityManagerService;
75import com.android.server.pm.PackageManagerService;
76import com.android.server.pm.UserManagerService;
77import com.google.android.collect.Lists;
78import com.google.android.collect.Maps;
79
Paul Lawrence8e397362014-01-27 15:22:30 -080080import org.apache.commons.codec.binary.Hex;
Paul Lawrence945490c2014-03-27 16:37:28 +000081import org.apache.commons.codec.DecoderException;
Mike Lockwood2f6a3882011-05-09 19:08:06 -070082import org.xmlpull.v1.XmlPullParserException;
Kenny Roota02b8b02010-08-05 16:14:17 -070083
Jeff Sharkeyb049e212012-09-07 23:16:01 -070084import java.io.File;
Kenny Root38cf8862010-09-26 14:18:51 -070085import java.io.FileDescriptor;
Kenny Root05105f72010-09-22 17:29:43 -070086import java.io.IOException;
Kenny Root38cf8862010-09-26 14:18:51 -070087import java.io.PrintWriter;
Kenny Root3b1abba2010-10-13 15:00:07 -070088import java.math.BigInteger;
Paul Lawrence8e397362014-01-27 15:22:30 -080089import java.nio.charset.StandardCharsets;
Kenny Root735de3b2010-09-30 14:11:39 -070090import java.security.NoSuchAlgorithmException;
Kenny Root3b1abba2010-10-13 15:00:07 -070091import java.security.spec.InvalidKeySpecException;
92import java.security.spec.KeySpec;
San Mehat22dd86e2010-01-12 12:21:18 -080093import java.util.ArrayList;
Kenny Roota02b8b02010-08-05 16:14:17 -070094import java.util.HashMap;
San Mehat6cdd9c02010-02-09 14:45:20 -080095import java.util.HashSet;
Kenny Root38cf8862010-09-26 14:18:51 -070096import java.util.Iterator;
Kenny Roota02b8b02010-08-05 16:14:17 -070097import java.util.LinkedList;
98import java.util.List;
Elliott Hughesf839b4f2014-09-26 12:30:47 -070099import java.util.Locale;
Kenny Roota02b8b02010-08-05 16:14:17 -0700100import java.util.Map;
Kenny Root38cf8862010-09-26 14:18:51 -0700101import java.util.Map.Entry;
Rickard Helldin5fb5df02014-02-07 09:35:04 +0100102import java.util.concurrent.atomic.AtomicInteger;
Kenny Root51a573c2012-05-17 13:30:28 -0700103import java.util.concurrent.CountDownLatch;
104import java.util.concurrent.TimeUnit;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800105
Kenny Root3b1abba2010-10-13 15:00:07 -0700106import javax.crypto.SecretKey;
107import javax.crypto.SecretKeyFactory;
108import javax.crypto.spec.PBEKeySpec;
109
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110/**
San Mehatb1043402010-02-05 08:26:50 -0800111 * MountService implements back-end services for platform storage
112 * management.
113 * @hide - Applications should use android.os.storage.StorageManager
114 * to access the MountService.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115 */
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -0700116class MountService extends IMountService.Stub
117 implements INativeDaemonConnectorCallbacks, Watchdog.Monitor {
Jason parks5af0b912010-11-29 09:05:25 -0600118
Christopher Tated417d622013-08-19 16:14:25 -0700119 // Static direct instance pointer for the tightly-coupled idle service to use
120 static MountService sSelf = null;
121
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700122 // TODO: listen for user creation/deletion
123
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800124 private static final boolean LOCAL_LOGD = false;
125 private static final boolean DEBUG_UNMOUNT = false;
126 private static final boolean DEBUG_EVENTS = false;
Kenny Rootb7db2722011-01-25 16:39:35 -0800127 private static final boolean DEBUG_OBB = false;
Kenny Root02c87302010-07-01 08:10:18 -0700128
Kenny Root07714d42011-08-17 17:49:28 -0700129 // Disable this since it messes up long-running cryptfs operations.
130 private static final boolean WATCHDOG_ENABLE = false;
131
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800132 private static final String TAG = "MountService";
133
Kenny Root305bcbf2010-09-03 07:56:38 -0700134 private static final String VOLD_TAG = "VoldConnector";
135
Kenny Rootcf0b38c2011-03-22 14:17:59 -0700136 /** Maximum number of ASEC containers allowed to be mounted. */
137 private static final int MAX_CONTAINERS = 250;
138
San Mehat4270e1e2010-01-29 05:32:19 -0800139 /*
140 * Internal vold volume state constants
141 */
San Mehat7fd0fee2009-12-17 07:12:23 -0800142 class VolumeState {
143 public static final int Init = -1;
144 public static final int NoMedia = 0;
145 public static final int Idle = 1;
146 public static final int Pending = 2;
147 public static final int Checking = 3;
148 public static final int Mounted = 4;
149 public static final int Unmounting = 5;
150 public static final int Formatting = 6;
151 public static final int Shared = 7;
152 public static final int SharedMnt = 8;
153 }
154
San Mehat4270e1e2010-01-29 05:32:19 -0800155 /*
156 * Internal vold response code constants
157 */
San Mehat22dd86e2010-01-12 12:21:18 -0800158 class VoldResponseCode {
San Mehat4270e1e2010-01-29 05:32:19 -0800159 /*
160 * 100 series - Requestion action was initiated; expect another reply
161 * before proceeding with a new command.
162 */
San Mehat22dd86e2010-01-12 12:21:18 -0800163 public static final int VolumeListResult = 110;
164 public static final int AsecListResult = 111;
San Mehatc1b4ce92010-02-16 17:13:03 -0800165 public static final int StorageUsersListResult = 112;
Paul Lawrencee51dcf92014-03-18 10:56:00 -0700166 public static final int CryptfsGetfieldResult = 113;
San Mehat22dd86e2010-01-12 12:21:18 -0800167
San Mehat4270e1e2010-01-29 05:32:19 -0800168 /*
169 * 200 series - Requestion action has been successfully completed.
170 */
171 public static final int ShareStatusResult = 210;
San Mehat22dd86e2010-01-12 12:21:18 -0800172 public static final int AsecPathResult = 211;
San Mehat4270e1e2010-01-29 05:32:19 -0800173 public static final int ShareEnabledResult = 212;
San Mehat22dd86e2010-01-12 12:21:18 -0800174
San Mehat4270e1e2010-01-29 05:32:19 -0800175 /*
176 * 400 series - Command was accepted, but the requested action
177 * did not take place.
178 */
179 public static final int OpFailedNoMedia = 401;
180 public static final int OpFailedMediaBlank = 402;
181 public static final int OpFailedMediaCorrupt = 403;
182 public static final int OpFailedVolNotMounted = 404;
San Mehatd9709982010-02-18 11:43:03 -0800183 public static final int OpFailedStorageBusy = 405;
San Mehat2d66cef2010-03-23 11:12:52 -0700184 public static final int OpFailedStorageNotFound = 406;
San Mehat4270e1e2010-01-29 05:32:19 -0800185
186 /*
187 * 600 series - Unsolicited broadcasts.
188 */
San Mehat22dd86e2010-01-12 12:21:18 -0800189 public static final int VolumeStateChange = 605;
Jeff Sharkey5aca2b82013-10-16 16:21:54 -0700190 public static final int VolumeUuidChange = 613;
191 public static final int VolumeUserLabelChange = 614;
San Mehat22dd86e2010-01-12 12:21:18 -0800192 public static final int VolumeDiskInserted = 630;
193 public static final int VolumeDiskRemoved = 631;
194 public static final int VolumeBadRemoval = 632;
Svetoslavf23b64d2013-04-25 14:45:54 -0700195
196 /*
197 * 700 series - fstrim
198 */
199 public static final int FstrimCompleted = 700;
San Mehat22dd86e2010-01-12 12:21:18 -0800200 }
201
Paul Lawrence8e397362014-01-27 15:22:30 -0800202 /** List of crypto types.
203 * These must match CRYPT_TYPE_XXX in cryptfs.h AND their
204 * corresponding commands in CommandListener.cpp */
205 public static final String[] CRYPTO_TYPES
206 = { "password", "default", "pattern", "pin" };
207
Brian Carlstrom7395a8a2014-04-28 22:11:01 -0700208 private final Context mContext;
Brian Carlstromdfad99a2014-05-07 15:21:14 -0700209 private final NativeDaemonConnector mConnector;
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700210
211 private final Object mVolumesLock = new Object();
212
213 /** When defined, base template for user-specific {@link StorageVolume}. */
214 private StorageVolume mEmulatedTemplate;
215
Jeff Sharkey1abdb712013-08-11 16:28:14 -0700216 // TODO: separate storage volumes on per-user basis
217
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -0800218 @GuardedBy("mVolumesLock")
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700219 private final ArrayList<StorageVolume> mVolumes = Lists.newArrayList();
220 /** Map from path to {@link StorageVolume} */
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -0800221 @GuardedBy("mVolumesLock")
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700222 private final HashMap<String, StorageVolume> mVolumesByPath = Maps.newHashMap();
223 /** Map from path to state */
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -0800224 @GuardedBy("mVolumesLock")
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700225 private final HashMap<String, String> mVolumeStates = Maps.newHashMap();
226
227 private volatile boolean mSystemReady = false;
228
San Mehat4270e1e2010-01-29 05:32:19 -0800229 private PackageManagerService mPms;
230 private boolean mUmsEnabling;
Mike Lockwoodecedfdc2011-06-08 15:11:59 -0700231 private boolean mUmsAvailable = false;
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800232 // Used as a lock for methods that register/unregister listeners.
233 final private ArrayList<MountServiceBinderListener> mListeners =
234 new ArrayList<MountServiceBinderListener>();
Jeff Sharkey0be607c2012-11-14 14:39:19 -0800235 private final CountDownLatch mConnectedSignal = new CountDownLatch(1);
236 private final CountDownLatch mAsecsScanned = new CountDownLatch(1);
San Mehat6a965af22010-02-24 17:47:30 -0800237 private boolean mSendUmsConnectedOnBoot = false;
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -0800238
San Mehat6cdd9c02010-02-09 14:45:20 -0800239 /**
240 * Private hash of currently mounted secure containers.
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800241 * Used as a lock in methods to manipulate secure containers.
San Mehat6cdd9c02010-02-09 14:45:20 -0800242 */
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800243 final private HashSet<String> mAsecMountSet = new HashSet<String>();
San Mehat6cdd9c02010-02-09 14:45:20 -0800244
Kenny Root02c87302010-07-01 08:10:18 -0700245 /**
Kenny Root3b1abba2010-10-13 15:00:07 -0700246 * The size of the crypto algorithm key in bits for OBB files. Currently
247 * Twofish is used which takes 128-bit keys.
248 */
249 private static final int CRYPTO_ALGORITHM_KEY_SIZE = 128;
250
251 /**
252 * The number of times to run SHA1 in the PBKDF2 function for OBB files.
253 * 1024 is reasonably secure and not too slow.
254 */
255 private static final int PBKDF2_HASH_ROUNDS = 1024;
256
257 /**
Kenny Roota02b8b02010-08-05 16:14:17 -0700258 * Mounted OBB tracking information. Used to track the current state of all
259 * OBBs.
Kenny Root02c87302010-07-01 08:10:18 -0700260 */
Kenny Root735de3b2010-09-30 14:11:39 -0700261 final private Map<IBinder, List<ObbState>> mObbMounts = new HashMap<IBinder, List<ObbState>>();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700262
263 /** Map from raw paths to {@link ObbState}. */
Kenny Roota02b8b02010-08-05 16:14:17 -0700264 final private Map<String, ObbState> mObbPathToStateMap = new HashMap<String, ObbState>();
265
266 class ObbState implements IBinder.DeathRecipient {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700267 public ObbState(String rawPath, String canonicalPath, int callingUid,
268 IObbActionListener token, int nonce) {
269 this.rawPath = rawPath;
270 this.canonicalPath = canonicalPath.toString();
271
272 final int userId = UserHandle.getUserId(callingUid);
273 this.ownerPath = buildObbPath(canonicalPath, userId, false);
274 this.voldPath = buildObbPath(canonicalPath, userId, true);
275
276 this.ownerGid = UserHandle.getSharedAppGid(callingUid);
Kenny Rootaf9d6672010-10-08 09:21:39 -0700277 this.token = token;
278 this.nonce = nonce;
Kenny Roota02b8b02010-08-05 16:14:17 -0700279 }
280
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700281 final String rawPath;
282 final String canonicalPath;
283 final String ownerPath;
284 final String voldPath;
Kenny Roota02b8b02010-08-05 16:14:17 -0700285
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700286 final int ownerGid;
Kenny Roota02b8b02010-08-05 16:14:17 -0700287
Kenny Rootaf9d6672010-10-08 09:21:39 -0700288 // Token of remote Binder caller
289 final IObbActionListener token;
290
291 // Identifier to pass back to the token
292 final int nonce;
Kenny Roota02b8b02010-08-05 16:14:17 -0700293
Kenny Root735de3b2010-09-30 14:11:39 -0700294 public IBinder getBinder() {
295 return token.asBinder();
296 }
297
Kenny Roota02b8b02010-08-05 16:14:17 -0700298 @Override
299 public void binderDied() {
300 ObbAction action = new UnmountObbAction(this, true);
301 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
Kenny Root735de3b2010-09-30 14:11:39 -0700302 }
Kenny Roota02b8b02010-08-05 16:14:17 -0700303
Kenny Root5919ac62010-10-05 09:49:40 -0700304 public void link() throws RemoteException {
305 getBinder().linkToDeath(this, 0);
306 }
307
308 public void unlink() {
Kenny Root735de3b2010-09-30 14:11:39 -0700309 getBinder().unlinkToDeath(this, 0);
Kenny Roota02b8b02010-08-05 16:14:17 -0700310 }
Kenny Root38cf8862010-09-26 14:18:51 -0700311
312 @Override
313 public String toString() {
314 StringBuilder sb = new StringBuilder("ObbState{");
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700315 sb.append("rawPath=").append(rawPath);
316 sb.append(",canonicalPath=").append(canonicalPath);
317 sb.append(",ownerPath=").append(ownerPath);
318 sb.append(",voldPath=").append(voldPath);
319 sb.append(",ownerGid=").append(ownerGid);
320 sb.append(",token=").append(token);
321 sb.append(",binder=").append(getBinder());
Kenny Root38cf8862010-09-26 14:18:51 -0700322 sb.append('}');
323 return sb.toString();
324 }
Kenny Roota02b8b02010-08-05 16:14:17 -0700325 }
326
327 // OBB Action Handler
328 final private ObbActionHandler mObbActionHandler;
329
330 // OBB action handler messages
331 private static final int OBB_RUN_ACTION = 1;
332 private static final int OBB_MCS_BOUND = 2;
333 private static final int OBB_MCS_UNBIND = 3;
334 private static final int OBB_MCS_RECONNECT = 4;
Kenny Rootaf9d6672010-10-08 09:21:39 -0700335 private static final int OBB_FLUSH_MOUNT_STATE = 5;
Kenny Roota02b8b02010-08-05 16:14:17 -0700336
337 /*
338 * Default Container Service information
339 */
340 static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
341 "com.android.defcontainer", "com.android.defcontainer.DefaultContainerService");
342
343 final private DefaultContainerConnection mDefContainerConn = new DefaultContainerConnection();
344
345 class DefaultContainerConnection implements ServiceConnection {
346 public void onServiceConnected(ComponentName name, IBinder service) {
347 if (DEBUG_OBB)
348 Slog.i(TAG, "onServiceConnected");
349 IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(service);
350 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_MCS_BOUND, imcs));
351 }
352
353 public void onServiceDisconnected(ComponentName name) {
354 if (DEBUG_OBB)
355 Slog.i(TAG, "onServiceDisconnected");
356 }
357 };
358
359 // Used in the ObbActionHandler
360 private IMediaContainerService mContainerService = null;
Kenny Root02c87302010-07-01 08:10:18 -0700361
362 // Handler messages
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800363 private static final int H_UNMOUNT_PM_UPDATE = 1;
364 private static final int H_UNMOUNT_PM_DONE = 2;
365 private static final int H_UNMOUNT_MS = 3;
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700366 private static final int H_SYSTEM_READY = 4;
Christopher Tated417d622013-08-19 16:14:25 -0700367 private static final int H_FSTRIM = 5;
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700368
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800369 private static final int RETRY_UNMOUNT_DELAY = 30; // in ms
370 private static final int MAX_UNMOUNT_RETRIES = 4;
371
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800372 class UnmountCallBack {
Kenny Root05105f72010-09-22 17:29:43 -0700373 final String path;
374 final boolean force;
Ben Komalo13c71972011-09-07 16:35:56 -0700375 final boolean removeEncryption;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800376 int retries;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800377
Ben Komalo13c71972011-09-07 16:35:56 -0700378 UnmountCallBack(String path, boolean force, boolean removeEncryption) {
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800379 retries = 0;
380 this.path = path;
381 this.force = force;
Ben Komalo13c71972011-09-07 16:35:56 -0700382 this.removeEncryption = removeEncryption;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800383 }
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800384
385 void handleFinished() {
San Mehata5078592010-03-25 09:36:54 -0700386 if (DEBUG_UNMOUNT) Slog.i(TAG, "Unmounting " + path);
Ben Komalo13c71972011-09-07 16:35:56 -0700387 doUnmountVolume(path, true, removeEncryption);
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800388 }
389 }
390
391 class UmsEnableCallBack extends UnmountCallBack {
Kenny Root05105f72010-09-22 17:29:43 -0700392 final String method;
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800393
394 UmsEnableCallBack(String path, String method, boolean force) {
Ben Komalo13c71972011-09-07 16:35:56 -0700395 super(path, force, false);
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800396 this.method = method;
397 }
398
399 @Override
400 void handleFinished() {
401 super.handleFinished();
402 doShareUnshareVolume(path, method, true);
403 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800404 }
405
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800406 class ShutdownCallBack extends UnmountCallBack {
Rickard Helldin5fb5df02014-02-07 09:35:04 +0100407 MountShutdownLatch mMountShutdownLatch;
408 ShutdownCallBack(String path, final MountShutdownLatch mountShutdownLatch) {
Ben Komalo13c71972011-09-07 16:35:56 -0700409 super(path, true, false);
Rickard Helldin5fb5df02014-02-07 09:35:04 +0100410 mMountShutdownLatch = mountShutdownLatch;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800411 }
412
413 @Override
414 void handleFinished() {
Ben Komalo13c71972011-09-07 16:35:56 -0700415 int ret = doUnmountVolume(path, true, removeEncryption);
Rickard Helldin5fb5df02014-02-07 09:35:04 +0100416 Slog.i(TAG, "Unmount completed: " + path + ", result code: " + ret);
417 mMountShutdownLatch.countDown();
418 }
419 }
420
421 static class MountShutdownLatch {
422 private IMountShutdownObserver mObserver;
423 private AtomicInteger mCount;
424
425 MountShutdownLatch(final IMountShutdownObserver observer, int count) {
426 mObserver = observer;
427 mCount = new AtomicInteger(count);
428 }
429
430 void countDown() {
431 boolean sendShutdown = false;
432 if (mCount.decrementAndGet() == 0) {
433 sendShutdown = true;
434 }
435 if (sendShutdown && mObserver != null) {
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800436 try {
Rickard Helldin5fb5df02014-02-07 09:35:04 +0100437 mObserver.onShutDownComplete(StorageResultCode.OperationSucceeded);
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800438 } catch (RemoteException e) {
San Mehata5078592010-03-25 09:36:54 -0700439 Slog.w(TAG, "RemoteException when shutting down");
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800440 }
441 }
442 }
443 }
444
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400445 class MountServiceHandler extends Handler {
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800446 ArrayList<UnmountCallBack> mForceUnmounts = new ArrayList<UnmountCallBack>();
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700447 boolean mUpdatingStatus = false;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800448
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400449 MountServiceHandler(Looper l) {
450 super(l);
451 }
452
Jason parks5af0b912010-11-29 09:05:25 -0600453 @Override
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800454 public void handleMessage(Message msg) {
455 switch (msg.what) {
456 case H_UNMOUNT_PM_UPDATE: {
San Mehata5078592010-03-25 09:36:54 -0700457 if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_UPDATE");
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800458 UnmountCallBack ucb = (UnmountCallBack) msg.obj;
459 mForceUnmounts.add(ucb);
San Mehata5078592010-03-25 09:36:54 -0700460 if (DEBUG_UNMOUNT) Slog.i(TAG, " registered = " + mUpdatingStatus);
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800461 // Register only if needed.
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700462 if (!mUpdatingStatus) {
San Mehata5078592010-03-25 09:36:54 -0700463 if (DEBUG_UNMOUNT) Slog.i(TAG, "Updating external media status on PackageManager");
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700464 mUpdatingStatus = true;
465 mPms.updateExternalMediaStatus(false, true);
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800466 }
467 break;
468 }
469 case H_UNMOUNT_PM_DONE: {
San Mehata5078592010-03-25 09:36:54 -0700470 if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_DONE");
San Mehata5078592010-03-25 09:36:54 -0700471 if (DEBUG_UNMOUNT) Slog.i(TAG, "Updated status. Processing requests");
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700472 mUpdatingStatus = false;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800473 int size = mForceUnmounts.size();
474 int sizeArr[] = new int[size];
475 int sizeArrN = 0;
Suchi Amalapurapu7af074a2010-04-05 16:46:32 -0700476 // Kill processes holding references first
477 ActivityManagerService ams = (ActivityManagerService)
478 ServiceManager.getService("activity");
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800479 for (int i = 0; i < size; i++) {
480 UnmountCallBack ucb = mForceUnmounts.get(i);
481 String path = ucb.path;
482 boolean done = false;
483 if (!ucb.force) {
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800484 done = true;
485 } else {
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800486 int pids[] = getStorageUsers(path);
487 if (pids == null || pids.length == 0) {
488 done = true;
489 } else {
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800490 // Eliminate system process here?
Dianne Hackborn64825172011-03-02 21:32:58 -0800491 ams.killPids(pids, "unmount media", true);
Suchi Amalapurapu7af074a2010-04-05 16:46:32 -0700492 // Confirm if file references have been freed.
493 pids = getStorageUsers(path);
494 if (pids == null || pids.length == 0) {
495 done = true;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800496 }
497 }
498 }
Suchi Amalapurapu7af074a2010-04-05 16:46:32 -0700499 if (!done && (ucb.retries < MAX_UNMOUNT_RETRIES)) {
500 // Retry again
501 Slog.i(TAG, "Retrying to kill storage users again");
502 mHandler.sendMessageDelayed(
503 mHandler.obtainMessage(H_UNMOUNT_PM_DONE,
504 ucb.retries++),
505 RETRY_UNMOUNT_DELAY);
506 } else {
507 if (ucb.retries >= MAX_UNMOUNT_RETRIES) {
508 Slog.i(TAG, "Failed to unmount media inspite of " +
509 MAX_UNMOUNT_RETRIES + " retries. Forcibly killing processes now");
510 }
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800511 sizeArr[sizeArrN++] = i;
512 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_MS,
513 ucb));
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800514 }
515 }
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800516 // Remove already processed elements from list.
517 for (int i = (sizeArrN-1); i >= 0; i--) {
518 mForceUnmounts.remove(sizeArr[i]);
519 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800520 break;
521 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700522 case H_UNMOUNT_MS: {
San Mehata5078592010-03-25 09:36:54 -0700523 if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_MS");
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800524 UnmountCallBack ucb = (UnmountCallBack) msg.obj;
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800525 ucb.handleFinished();
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800526 break;
527 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700528 case H_SYSTEM_READY: {
529 try {
530 handleSystemReady();
531 } catch (Exception ex) {
532 Slog.e(TAG, "Boot-time mount exception", ex);
533 }
534 break;
535 }
Christopher Tated417d622013-08-19 16:14:25 -0700536 case H_FSTRIM: {
537 waitForReady();
538 Slog.i(TAG, "Running fstrim idle maintenance");
539 try {
540 // This method must be run on the main (handler) thread,
541 // so it is safe to directly call into vold.
542 mConnector.execute("fstrim", "dotrim");
543 EventLogTags.writeFstrimStart(SystemClock.elapsedRealtime());
544 } catch (NativeDaemonConnectorException ndce) {
545 Slog.e(TAG, "Failed to run fstrim!");
546 }
547 // invoke the completion callback, if any
548 Runnable callback = (Runnable) msg.obj;
549 if (callback != null) {
550 callback.run();
551 }
552 break;
553 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800554 }
555 }
556 };
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700557
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700558 private final Handler mHandler;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800559
Kenny Root51a573c2012-05-17 13:30:28 -0700560 void waitForAsecScan() {
561 waitForLatch(mAsecsScanned);
562 }
563
San Mehat207e5382010-02-04 20:46:54 -0800564 private void waitForReady() {
Kenny Root51a573c2012-05-17 13:30:28 -0700565 waitForLatch(mConnectedSignal);
566 }
567
568 private void waitForLatch(CountDownLatch latch) {
Kenny Root51a573c2012-05-17 13:30:28 -0700569 for (;;) {
570 try {
571 if (latch.await(5000, TimeUnit.MILLISECONDS)) {
San Mehat207e5382010-02-04 20:46:54 -0800572 return;
Kenny Root51a573c2012-05-17 13:30:28 -0700573 } else {
574 Slog.w(TAG, "Thread " + Thread.currentThread().getName()
575 + " still waiting for MountService ready...");
San Mehat207e5382010-02-04 20:46:54 -0800576 }
Kenny Root51a573c2012-05-17 13:30:28 -0700577 } catch (InterruptedException e) {
578 Slog.w(TAG, "Interrupt while waiting for MountService to be ready.");
San Mehat207e5382010-02-04 20:46:54 -0800579 }
San Mehat207e5382010-02-04 20:46:54 -0800580 }
San Mehat1f6301e2010-01-07 22:40:27 -0800581 }
Kenny Root02c87302010-07-01 08:10:18 -0700582
Paul Lawrence945490c2014-03-27 16:37:28 +0000583 private boolean isReady() {
584 try {
585 return mConnectedSignal.await(0, TimeUnit.MILLISECONDS);
586 } catch (InterruptedException e) {
587 return false;
588 }
589 }
590
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700591 private void handleSystemReady() {
592 // Snapshot current volume states since it's not safe to call into vold
593 // while holding locks.
594 final HashMap<String, String> snapshot;
595 synchronized (mVolumesLock) {
596 snapshot = new HashMap<String, String>(mVolumeStates);
597 }
598
599 for (Map.Entry<String, String> entry : snapshot.entrySet()) {
600 final String path = entry.getKey();
601 final String state = entry.getValue();
602
603 if (state.equals(Environment.MEDIA_UNMOUNTED)) {
604 int rc = doMountVolume(path);
605 if (rc != StorageResultCode.OperationSucceeded) {
606 Slog.e(TAG, String.format("Boot-time mount failed (%d)",
607 rc));
608 }
609 } else if (state.equals(Environment.MEDIA_SHARED)) {
610 /*
611 * Bootstrap UMS enabled state since vold indicates
612 * the volume is shared (runtime restart while ums enabled)
613 */
614 notifyVolumeStateChange(null, path, VolumeState.NoMedia,
615 VolumeState.Shared);
616 }
617 }
618
619 // Push mounted state for all emulated storage
620 synchronized (mVolumesLock) {
621 for (StorageVolume volume : mVolumes) {
622 if (volume.isEmulated()) {
623 updatePublicVolumeState(volume, Environment.MEDIA_MOUNTED);
624 }
625 }
626 }
627
628 /*
629 * If UMS was connected on boot, send the connected event
630 * now that we're up.
631 */
632 if (mSendUmsConnectedOnBoot) {
633 sendUmsIntent(true);
634 mSendUmsConnectedOnBoot = false;
635 }
Christopher Tate115afda2014-06-06 19:06:26 -0700636
637 /*
638 * Start scheduling nominally-daily fstrim operations
639 */
640 MountServiceIdler.scheduleIdlePass(mContext);
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700641 }
642
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700643 private final BroadcastReceiver mUserReceiver = new BroadcastReceiver() {
644 @Override
645 public void onReceive(Context context, Intent intent) {
646 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
647 if (userId == -1) return;
648 final UserHandle user = new UserHandle(userId);
649
650 final String action = intent.getAction();
651 if (Intent.ACTION_USER_ADDED.equals(action)) {
652 synchronized (mVolumesLock) {
653 createEmulatedVolumeForUserLocked(user);
654 }
655
656 } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
657 synchronized (mVolumesLock) {
658 final List<StorageVolume> toRemove = Lists.newArrayList();
659 for (StorageVolume volume : mVolumes) {
660 if (user.equals(volume.getOwner())) {
661 toRemove.add(volume);
662 }
663 }
664 for (StorageVolume volume : toRemove) {
665 removeVolumeLocked(volume);
666 }
667 }
668 }
669 }
670 };
671
672 private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
673 @Override
674 public void onReceive(Context context, Intent intent) {
675 boolean available = (intent.getBooleanExtra(UsbManager.USB_CONNECTED, false) &&
676 intent.getBooleanExtra(UsbManager.USB_FUNCTION_MASS_STORAGE, false));
677 notifyShareAvailabilityChange(available);
678 }
679 };
680
San Mehat4270e1e2010-01-29 05:32:19 -0800681 private final class MountServiceBinderListener implements IBinder.DeathRecipient {
682 final IMountServiceListener mListener;
683
684 MountServiceBinderListener(IMountServiceListener listener) {
685 mListener = listener;
Kenny Root02c87302010-07-01 08:10:18 -0700686
San Mehat91c77612010-01-07 10:39:41 -0800687 }
688
San Mehat4270e1e2010-01-29 05:32:19 -0800689 public void binderDied() {
San Mehata5078592010-03-25 09:36:54 -0700690 if (LOCAL_LOGD) Slog.d(TAG, "An IMountServiceListener has died!");
Kenny Roota02b8b02010-08-05 16:14:17 -0700691 synchronized (mListeners) {
San Mehat4270e1e2010-01-29 05:32:19 -0800692 mListeners.remove(this);
693 mListener.asBinder().unlinkToDeath(this, 0);
694 }
695 }
696 }
697
Christopher Tated417d622013-08-19 16:14:25 -0700698 void runIdleMaintenance(Runnable callback) {
699 mHandler.sendMessage(mHandler.obtainMessage(H_FSTRIM, callback));
700 }
701
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800702 private void doShareUnshareVolume(String path, String method, boolean enable) {
San Mehat4270e1e2010-01-29 05:32:19 -0800703 // TODO: Add support for multiple share methods
704 if (!method.equals("ums")) {
705 throw new IllegalArgumentException(String.format("Method %s not supported", method));
706 }
707
San Mehat4270e1e2010-01-29 05:32:19 -0800708 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -0800709 mConnector.execute("volume", enable ? "share" : "unshare", path, method);
San Mehat4270e1e2010-01-29 05:32:19 -0800710 } catch (NativeDaemonConnectorException e) {
San Mehata5078592010-03-25 09:36:54 -0700711 Slog.e(TAG, "Failed to share/unshare", e);
San Mehat4270e1e2010-01-29 05:32:19 -0800712 }
San Mehat4270e1e2010-01-29 05:32:19 -0800713 }
714
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700715 private void updatePublicVolumeState(StorageVolume volume, String state) {
716 final String path = volume.getPath();
717 final String oldState;
718 synchronized (mVolumesLock) {
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400719 oldState = mVolumeStates.put(path, state);
Jeff Sharkey1f706c62013-10-17 10:52:17 -0700720 volume.setState(state);
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400721 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700722
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400723 if (state.equals(oldState)) {
724 Slog.w(TAG, String.format("Duplicate state transition (%s -> %s) for %s",
725 state, state, path));
San Mehat4270e1e2010-01-29 05:32:19 -0800726 return;
727 }
San Mehatb1043402010-02-05 08:26:50 -0800728
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400729 Slog.d(TAG, "volume state changed for " + path + " (" + oldState + " -> " + state + ")");
Kenny Rootaf9d6672010-10-08 09:21:39 -0700730
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700731 // Tell PackageManager about changes to primary volume state, but only
732 // when not emulated.
733 if (volume.isPrimary() && !volume.isEmulated()) {
734 if (Environment.MEDIA_UNMOUNTED.equals(state)) {
735 mPms.updateExternalMediaStatus(false, false);
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400736
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700737 /*
738 * Some OBBs might have been unmounted when this volume was
739 * unmounted, so send a message to the handler to let it know to
740 * remove those from the list of mounted OBBS.
741 */
742 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(
743 OBB_FLUSH_MOUNT_STATE, path));
744 } else if (Environment.MEDIA_MOUNTED.equals(state)) {
745 mPms.updateExternalMediaStatus(true, false);
Mike Lockwood03559752010-07-19 18:25:03 -0400746 }
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800747 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700748
San Mehat4270e1e2010-01-29 05:32:19 -0800749 synchronized (mListeners) {
750 for (int i = mListeners.size() -1; i >= 0; i--) {
751 MountServiceBinderListener bl = mListeners.get(i);
752 try {
San Mehatb1043402010-02-05 08:26:50 -0800753 bl.mListener.onStorageStateChanged(path, oldState, state);
San Mehat4270e1e2010-01-29 05:32:19 -0800754 } catch (RemoteException rex) {
San Mehata5078592010-03-25 09:36:54 -0700755 Slog.e(TAG, "Listener dead");
San Mehat4270e1e2010-01-29 05:32:19 -0800756 mListeners.remove(i);
757 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700758 Slog.e(TAG, "Listener failed", ex);
San Mehat4270e1e2010-01-29 05:32:19 -0800759 }
760 }
761 }
762 }
763
764 /**
San Mehat4270e1e2010-01-29 05:32:19 -0800765 * Callback from NativeDaemonConnector
766 */
767 public void onDaemonConnected() {
768 /*
769 * Since we'll be calling back into the NativeDaemonConnector,
770 * we need to do our work in a new thread.
771 */
Kenny Root51a573c2012-05-17 13:30:28 -0700772 new Thread("MountService#onDaemonConnected") {
Jason parks5af0b912010-11-29 09:05:25 -0600773 @Override
San Mehat4270e1e2010-01-29 05:32:19 -0800774 public void run() {
775 /**
776 * Determine media state and UMS detection status
777 */
San Mehat4270e1e2010-01-29 05:32:19 -0800778 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -0800779 final String[] vols = NativeDaemonEvent.filterMessageList(
JP Abgrall5f054ce2014-07-24 18:19:12 -0700780 mConnector.executeForList("volume", "list", "broadcast"),
Jeff Sharkeydd519fa2011-12-02 14:11:21 -0800781 VoldResponseCode.VolumeListResult);
San Mehat4270e1e2010-01-29 05:32:19 -0800782 for (String volstr : vols) {
783 String[] tok = volstr.split(" ");
784 // FMT: <label> <mountpoint> <state>
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400785 String path = tok[1];
786 String state = Environment.MEDIA_REMOVED;
787
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700788 final StorageVolume volume;
789 synchronized (mVolumesLock) {
790 volume = mVolumesByPath.get(path);
791 }
792
San Mehat4270e1e2010-01-29 05:32:19 -0800793 int st = Integer.parseInt(tok[2]);
794 if (st == VolumeState.NoMedia) {
795 state = Environment.MEDIA_REMOVED;
796 } else if (st == VolumeState.Idle) {
San Mehat207e5382010-02-04 20:46:54 -0800797 state = Environment.MEDIA_UNMOUNTED;
San Mehat4270e1e2010-01-29 05:32:19 -0800798 } else if (st == VolumeState.Mounted) {
799 state = Environment.MEDIA_MOUNTED;
San Mehata5078592010-03-25 09:36:54 -0700800 Slog.i(TAG, "Media already mounted on daemon connection");
San Mehat4270e1e2010-01-29 05:32:19 -0800801 } else if (st == VolumeState.Shared) {
802 state = Environment.MEDIA_SHARED;
San Mehata5078592010-03-25 09:36:54 -0700803 Slog.i(TAG, "Media shared on daemon connection");
San Mehat4270e1e2010-01-29 05:32:19 -0800804 } else {
805 throw new Exception(String.format("Unexpected state %d", st));
806 }
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400807
808 if (state != null) {
809 if (DEBUG_EVENTS) Slog.i(TAG, "Updating valid state " + state);
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700810 updatePublicVolumeState(volume, state);
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400811 }
San Mehat4270e1e2010-01-29 05:32:19 -0800812 }
813 } catch (Exception e) {
San Mehata5078592010-03-25 09:36:54 -0700814 Slog.e(TAG, "Error processing initial volume state", e);
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700815 final StorageVolume primary = getPrimaryPhysicalVolume();
816 if (primary != null) {
817 updatePublicVolumeState(primary, Environment.MEDIA_REMOVED);
818 }
San Mehat4270e1e2010-01-29 05:32:19 -0800819 }
820
San Mehat207e5382010-02-04 20:46:54 -0800821 /*
Jason parks9ed98bc2011-01-17 09:58:35 -0600822 * Now that we've done our initialization, release
San Mehat207e5382010-02-04 20:46:54 -0800823 * the hounds!
824 */
Kenny Root51a573c2012-05-17 13:30:28 -0700825 mConnectedSignal.countDown();
Kenny Root51a573c2012-05-17 13:30:28 -0700826
Elliott Hughesf839b4f2014-09-26 12:30:47 -0700827 // On an encrypted device we can't see system properties yet, so pull
828 // the system locale out of the mount service.
829 copyLocaleFromMountService();
830
Kenny Root51a573c2012-05-17 13:30:28 -0700831 // Let package manager load internal ASECs.
832 mPms.scanAvailableAsecs();
833
834 // Notify people waiting for ASECs to be scanned that it's done.
835 mAsecsScanned.countDown();
San Mehat4270e1e2010-01-29 05:32:19 -0800836 }
837 }.start();
838 }
839
Elliott Hughesf839b4f2014-09-26 12:30:47 -0700840 private void copyLocaleFromMountService() {
841 String systemLocale;
842 try {
843 systemLocale = getField(StorageManager.SYSTEM_LOCALE_KEY);
844 } catch (RemoteException e) {
845 return;
846 }
847 if (TextUtils.isEmpty(systemLocale)) {
848 return;
849 }
850
851 Slog.d(TAG, "Got locale " + systemLocale + " from mount service");
852 Locale locale = Locale.forLanguageTag(systemLocale);
853 Configuration config = new Configuration();
854 config.setLocale(locale);
855 try {
856 ActivityManagerNative.getDefault().updateConfiguration(config);
857 } catch (RemoteException e) {
858 Slog.e(TAG, "Error setting system locale from mount service", e);
859 }
860 }
861
San Mehat4270e1e2010-01-29 05:32:19 -0800862 /**
San Mehat4270e1e2010-01-29 05:32:19 -0800863 * Callback from NativeDaemonConnector
864 */
Dianne Hackborn77b987f2014-02-26 16:20:52 -0800865 public boolean onCheckHoldWakeLock(int code) {
866 return false;
867 }
868
869 /**
870 * Callback from NativeDaemonConnector
871 */
San Mehat4270e1e2010-01-29 05:32:19 -0800872 public boolean onEvent(int code, String raw, String[] cooked) {
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800873 if (DEBUG_EVENTS) {
874 StringBuilder builder = new StringBuilder();
875 builder.append("onEvent::");
876 builder.append(" raw= " + raw);
877 if (cooked != null) {
878 builder.append(" cooked = " );
879 for (String str : cooked) {
880 builder.append(" " + str);
881 }
882 }
San Mehata5078592010-03-25 09:36:54 -0700883 Slog.i(TAG, builder.toString());
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800884 }
San Mehat4270e1e2010-01-29 05:32:19 -0800885 if (code == VoldResponseCode.VolumeStateChange) {
886 /*
887 * One of the volumes we're managing has changed state.
888 * Format: "NNN Volume <label> <path> state changed
889 * from <old_#> (<old_str>) to <new_#> (<new_str>)"
890 */
891 notifyVolumeStateChange(
892 cooked[2], cooked[3], Integer.parseInt(cooked[7]),
893 Integer.parseInt(cooked[10]));
Jeff Sharkey5aca2b82013-10-16 16:21:54 -0700894 } else if (code == VoldResponseCode.VolumeUuidChange) {
895 // Format: nnn <label> <path> <uuid>
896 final String path = cooked[2];
897 final String uuid = (cooked.length > 3) ? cooked[3] : null;
898
899 final StorageVolume vol = mVolumesByPath.get(path);
900 if (vol != null) {
901 vol.setUuid(uuid);
902 }
903
904 } else if (code == VoldResponseCode.VolumeUserLabelChange) {
905 // Format: nnn <label> <path> <label>
906 final String path = cooked[2];
907 final String userLabel = (cooked.length > 3) ? cooked[3] : null;
908
909 final StorageVolume vol = mVolumesByPath.get(path);
910 if (vol != null) {
911 vol.setUserLabel(userLabel);
912 }
913
San Mehat4270e1e2010-01-29 05:32:19 -0800914 } else if ((code == VoldResponseCode.VolumeDiskInserted) ||
915 (code == VoldResponseCode.VolumeDiskRemoved) ||
916 (code == VoldResponseCode.VolumeBadRemoval)) {
917 // FMT: NNN Volume <label> <mountpoint> disk inserted (<major>:<minor>)
918 // FMT: NNN Volume <label> <mountpoint> disk removed (<major>:<minor>)
919 // FMT: NNN Volume <label> <mountpoint> bad removal (<major>:<minor>)
Mike Lockwooda5250c92011-05-23 13:44:04 -0400920 String action = null;
San Mehat4270e1e2010-01-29 05:32:19 -0800921 final String label = cooked[2];
922 final String path = cooked[3];
923 int major = -1;
924 int minor = -1;
925
926 try {
927 String devComp = cooked[6].substring(1, cooked[6].length() -1);
928 String[] devTok = devComp.split(":");
929 major = Integer.parseInt(devTok[0]);
930 minor = Integer.parseInt(devTok[1]);
931 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700932 Slog.e(TAG, "Failed to parse major/minor", ex);
San Mehat4270e1e2010-01-29 05:32:19 -0800933 }
934
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700935 final StorageVolume volume;
936 final String state;
937 synchronized (mVolumesLock) {
938 volume = mVolumesByPath.get(path);
939 state = mVolumeStates.get(path);
940 }
941
San Mehat4270e1e2010-01-29 05:32:19 -0800942 if (code == VoldResponseCode.VolumeDiskInserted) {
Dianne Hackborn8d044e82013-04-30 17:24:15 -0700943 new Thread("MountService#VolumeDiskInserted") {
Jason parks5af0b912010-11-29 09:05:25 -0600944 @Override
San Mehat4270e1e2010-01-29 05:32:19 -0800945 public void run() {
946 try {
947 int rc;
San Mehatb1043402010-02-05 08:26:50 -0800948 if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {
San Mehata5078592010-03-25 09:36:54 -0700949 Slog.w(TAG, String.format("Insertion mount failed (%d)", rc));
San Mehat4270e1e2010-01-29 05:32:19 -0800950 }
951 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700952 Slog.w(TAG, "Failed to mount media on insertion", ex);
San Mehat4270e1e2010-01-29 05:32:19 -0800953 }
954 }
955 }.start();
956 } else if (code == VoldResponseCode.VolumeDiskRemoved) {
957 /*
958 * This event gets trumped if we're already in BAD_REMOVAL state
959 */
960 if (getVolumeState(path).equals(Environment.MEDIA_BAD_REMOVAL)) {
961 return true;
962 }
963 /* Send the media unmounted event first */
San Mehata5078592010-03-25 09:36:54 -0700964 if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700965 updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
Johan Redestig0464c072014-01-18 22:46:56 +0100966 sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, volume, UserHandle.ALL);
San Mehat4270e1e2010-01-29 05:32:19 -0800967
San Mehata5078592010-03-25 09:36:54 -0700968 if (DEBUG_EVENTS) Slog.i(TAG, "Sending media removed");
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700969 updatePublicVolumeState(volume, Environment.MEDIA_REMOVED);
Mike Lockwooda5250c92011-05-23 13:44:04 -0400970 action = Intent.ACTION_MEDIA_REMOVED;
San Mehat4270e1e2010-01-29 05:32:19 -0800971 } else if (code == VoldResponseCode.VolumeBadRemoval) {
San Mehata5078592010-03-25 09:36:54 -0700972 if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
San Mehat4270e1e2010-01-29 05:32:19 -0800973 /* Send the media unmounted event first */
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700974 updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
Christian Beckf503c8f2013-05-20 08:42:45 +0200975 sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, volume, UserHandle.ALL);
San Mehat4270e1e2010-01-29 05:32:19 -0800976
San Mehata5078592010-03-25 09:36:54 -0700977 if (DEBUG_EVENTS) Slog.i(TAG, "Sending media bad removal");
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700978 updatePublicVolumeState(volume, Environment.MEDIA_BAD_REMOVAL);
Mike Lockwooda5250c92011-05-23 13:44:04 -0400979 action = Intent.ACTION_MEDIA_BAD_REMOVAL;
Svetoslavf23b64d2013-04-25 14:45:54 -0700980 } else if (code == VoldResponseCode.FstrimCompleted) {
Svetoslav9e814a82013-04-30 10:43:56 -0700981 EventLogTags.writeFstrimFinish(SystemClock.elapsedRealtime());
San Mehat4270e1e2010-01-29 05:32:19 -0800982 } else {
San Mehata5078592010-03-25 09:36:54 -0700983 Slog.e(TAG, String.format("Unknown code {%d}", code));
San Mehat4270e1e2010-01-29 05:32:19 -0800984 }
Mike Lockwooda5250c92011-05-23 13:44:04 -0400985
986 if (action != null) {
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700987 sendStorageIntent(action, volume, UserHandle.ALL);
Mike Lockwooda5250c92011-05-23 13:44:04 -0400988 }
San Mehat4270e1e2010-01-29 05:32:19 -0800989 } else {
990 return false;
991 }
992
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400993 return true;
San Mehat4270e1e2010-01-29 05:32:19 -0800994 }
995
San Mehat207e5382010-02-04 20:46:54 -0800996 private void notifyVolumeStateChange(String label, String path, int oldState, int newState) {
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700997 final StorageVolume volume;
998 final String state;
999 synchronized (mVolumesLock) {
1000 volume = mVolumesByPath.get(path);
1001 state = getVolumeState(path);
1002 }
1003
1004 if (DEBUG_EVENTS) Slog.i(TAG, "notifyVolumeStateChange::" + state);
San Mehat4270e1e2010-01-29 05:32:19 -08001005
Mike Lockwooda5250c92011-05-23 13:44:04 -04001006 String action = null;
San Mehat4270e1e2010-01-29 05:32:19 -08001007
Mike Lockwoodbf2dd442010-03-03 06:16:52 -05001008 if (oldState == VolumeState.Shared && newState != oldState) {
San Mehata5078592010-03-25 09:36:54 -07001009 if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_UNSHARED intent");
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001010 sendStorageIntent(Intent.ACTION_MEDIA_UNSHARED, volume, UserHandle.ALL);
Mike Lockwoodbf2dd442010-03-03 06:16:52 -05001011 }
1012
San Mehat4270e1e2010-01-29 05:32:19 -08001013 if (newState == VolumeState.Init) {
1014 } else if (newState == VolumeState.NoMedia) {
1015 // NoMedia is handled via Disk Remove events
1016 } else if (newState == VolumeState.Idle) {
1017 /*
1018 * Don't notify if we're in BAD_REMOVAL, NOFS, UNMOUNTABLE, or
1019 * if we're in the process of enabling UMS
1020 */
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001021 if (!state.equals(
1022 Environment.MEDIA_BAD_REMOVAL) && !state.equals(
1023 Environment.MEDIA_NOFS) && !state.equals(
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001024 Environment.MEDIA_UNMOUNTABLE) && !getUmsEnabling()) {
San Mehata5078592010-03-25 09:36:54 -07001025 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state for media bad removal nofs and unmountable");
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001026 updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
Mike Lockwooda5250c92011-05-23 13:44:04 -04001027 action = Intent.ACTION_MEDIA_UNMOUNTED;
San Mehat4270e1e2010-01-29 05:32:19 -08001028 }
1029 } else if (newState == VolumeState.Pending) {
1030 } else if (newState == VolumeState.Checking) {
San Mehata5078592010-03-25 09:36:54 -07001031 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state checking");
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001032 updatePublicVolumeState(volume, Environment.MEDIA_CHECKING);
Mike Lockwooda5250c92011-05-23 13:44:04 -04001033 action = Intent.ACTION_MEDIA_CHECKING;
San Mehat4270e1e2010-01-29 05:32:19 -08001034 } else if (newState == VolumeState.Mounted) {
San Mehata5078592010-03-25 09:36:54 -07001035 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state mounted");
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001036 updatePublicVolumeState(volume, Environment.MEDIA_MOUNTED);
Mike Lockwooda5250c92011-05-23 13:44:04 -04001037 action = Intent.ACTION_MEDIA_MOUNTED;
San Mehat4270e1e2010-01-29 05:32:19 -08001038 } else if (newState == VolumeState.Unmounting) {
Mike Lockwooda5250c92011-05-23 13:44:04 -04001039 action = Intent.ACTION_MEDIA_EJECT;
San Mehat4270e1e2010-01-29 05:32:19 -08001040 } else if (newState == VolumeState.Formatting) {
1041 } else if (newState == VolumeState.Shared) {
San Mehata5078592010-03-25 09:36:54 -07001042 if (DEBUG_EVENTS) Slog.i(TAG, "Updating volume state media mounted");
San Mehat4270e1e2010-01-29 05:32:19 -08001043 /* Send the media unmounted event first */
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001044 updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
1045 sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, volume, UserHandle.ALL);
San Mehat4270e1e2010-01-29 05:32:19 -08001046
San Mehata5078592010-03-25 09:36:54 -07001047 if (DEBUG_EVENTS) Slog.i(TAG, "Updating media shared");
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001048 updatePublicVolumeState(volume, Environment.MEDIA_SHARED);
Mike Lockwooda5250c92011-05-23 13:44:04 -04001049 action = Intent.ACTION_MEDIA_SHARED;
San Mehata5078592010-03-25 09:36:54 -07001050 if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_SHARED intent");
San Mehat4270e1e2010-01-29 05:32:19 -08001051 } else if (newState == VolumeState.SharedMnt) {
San Mehata5078592010-03-25 09:36:54 -07001052 Slog.e(TAG, "Live shared mounts not supported yet!");
San Mehat4270e1e2010-01-29 05:32:19 -08001053 return;
1054 } else {
San Mehata5078592010-03-25 09:36:54 -07001055 Slog.e(TAG, "Unhandled VolumeState {" + newState + "}");
San Mehat4270e1e2010-01-29 05:32:19 -08001056 }
1057
Mike Lockwooda5250c92011-05-23 13:44:04 -04001058 if (action != null) {
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001059 sendStorageIntent(action, volume, UserHandle.ALL);
San Mehat4270e1e2010-01-29 05:32:19 -08001060 }
1061 }
1062
San Mehat207e5382010-02-04 20:46:54 -08001063 private int doMountVolume(String path) {
San Mehatb1043402010-02-05 08:26:50 -08001064 int rc = StorageResultCode.OperationSucceeded;
San Mehat207e5382010-02-04 20:46:54 -08001065
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001066 final StorageVolume volume;
1067 synchronized (mVolumesLock) {
1068 volume = mVolumesByPath.get(path);
1069 }
1070
Emily Bernier92aa5a22014-07-07 10:11:48 -04001071 if (!volume.isEmulated() && hasUserRestriction(UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA)) {
1072 Slog.w(TAG, "User has restriction DISALLOW_MOUNT_PHYSICAL_MEDIA; cannot mount volume.");
1073 return StorageResultCode.OperationFailedInternalError;
1074 }
1075
San Mehata5078592010-03-25 09:36:54 -07001076 if (DEBUG_EVENTS) Slog.i(TAG, "doMountVolume: Mouting " + path);
San Mehat207e5382010-02-04 20:46:54 -08001077 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001078 mConnector.execute("volume", "mount", path);
San Mehat207e5382010-02-04 20:46:54 -08001079 } catch (NativeDaemonConnectorException e) {
1080 /*
1081 * Mount failed for some reason
1082 */
Mike Lockwooda5250c92011-05-23 13:44:04 -04001083 String action = null;
San Mehat207e5382010-02-04 20:46:54 -08001084 int code = e.getCode();
1085 if (code == VoldResponseCode.OpFailedNoMedia) {
1086 /*
1087 * Attempt to mount but no media inserted
1088 */
San Mehatb1043402010-02-05 08:26:50 -08001089 rc = StorageResultCode.OperationFailedNoMedia;
San Mehat207e5382010-02-04 20:46:54 -08001090 } else if (code == VoldResponseCode.OpFailedMediaBlank) {
San Mehata5078592010-03-25 09:36:54 -07001091 if (DEBUG_EVENTS) Slog.i(TAG, " updating volume state :: media nofs");
San Mehat207e5382010-02-04 20:46:54 -08001092 /*
1093 * Media is blank or does not contain a supported filesystem
1094 */
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001095 updatePublicVolumeState(volume, Environment.MEDIA_NOFS);
Mike Lockwooda5250c92011-05-23 13:44:04 -04001096 action = Intent.ACTION_MEDIA_NOFS;
San Mehatb1043402010-02-05 08:26:50 -08001097 rc = StorageResultCode.OperationFailedMediaBlank;
San Mehat207e5382010-02-04 20:46:54 -08001098 } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
San Mehata5078592010-03-25 09:36:54 -07001099 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state media corrupt");
San Mehat207e5382010-02-04 20:46:54 -08001100 /*
1101 * Volume consistency check failed
1102 */
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001103 updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTABLE);
Mike Lockwooda5250c92011-05-23 13:44:04 -04001104 action = Intent.ACTION_MEDIA_UNMOUNTABLE;
San Mehatb1043402010-02-05 08:26:50 -08001105 rc = StorageResultCode.OperationFailedMediaCorrupt;
San Mehat207e5382010-02-04 20:46:54 -08001106 } else {
San Mehatb1043402010-02-05 08:26:50 -08001107 rc = StorageResultCode.OperationFailedInternalError;
San Mehat207e5382010-02-04 20:46:54 -08001108 }
1109
1110 /*
1111 * Send broadcast intent (if required for the failure)
1112 */
Mike Lockwooda5250c92011-05-23 13:44:04 -04001113 if (action != null) {
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001114 sendStorageIntent(action, volume, UserHandle.ALL);
San Mehat207e5382010-02-04 20:46:54 -08001115 }
1116 }
1117
1118 return rc;
1119 }
1120
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -08001121 /*
1122 * If force is not set, we do not unmount if there are
1123 * processes holding references to the volume about to be unmounted.
1124 * If force is set, all the processes holding references need to be
1125 * killed via the ActivityManager before actually unmounting the volume.
1126 * This might even take a while and might be retried after timed delays
1127 * to make sure we dont end up in an instable state and kill some core
1128 * processes.
Ben Komalo13c71972011-09-07 16:35:56 -07001129 * If removeEncryption is set, force is implied, and the system will remove any encryption
1130 * mapping set on the volume when unmounting.
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -08001131 */
Ben Komalo13c71972011-09-07 16:35:56 -07001132 private int doUnmountVolume(String path, boolean force, boolean removeEncryption) {
San Mehat59443a62010-02-09 13:28:45 -08001133 if (!getVolumeState(path).equals(Environment.MEDIA_MOUNTED)) {
San Mehat207e5382010-02-04 20:46:54 -08001134 return VoldResponseCode.OpFailedVolNotMounted;
1135 }
Kenny Rootaa485402010-09-14 14:49:41 -07001136
1137 /*
1138 * Force a GC to make sure AssetManagers in other threads of the
1139 * system_server are cleaned up. We have to do this since AssetManager
1140 * instances are kept as a WeakReference and it's possible we have files
1141 * open on the external storage.
1142 */
1143 Runtime.getRuntime().gc();
1144
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -08001145 // Redundant probably. But no harm in updating state again.
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -07001146 mPms.updateExternalMediaStatus(false, false);
San Mehat207e5382010-02-04 20:46:54 -08001147 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001148 final Command cmd = new Command("volume", "unmount", path);
1149 if (removeEncryption) {
1150 cmd.appendArg("force_and_revert");
1151 } else if (force) {
1152 cmd.appendArg("force");
1153 }
1154 mConnector.execute(cmd);
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -07001155 // We unmounted the volume. None of the asec containers are available now.
1156 synchronized (mAsecMountSet) {
1157 mAsecMountSet.clear();
1158 }
San Mehatb1043402010-02-05 08:26:50 -08001159 return StorageResultCode.OperationSucceeded;
San Mehat207e5382010-02-04 20:46:54 -08001160 } catch (NativeDaemonConnectorException e) {
1161 // Don't worry about mismatch in PackageManager since the
1162 // call back will handle the status changes any way.
1163 int code = e.getCode();
1164 if (code == VoldResponseCode.OpFailedVolNotMounted) {
San Mehata181b212010-02-11 06:50:20 -08001165 return StorageResultCode.OperationFailedStorageNotMounted;
San Mehatd9709982010-02-18 11:43:03 -08001166 } else if (code == VoldResponseCode.OpFailedStorageBusy) {
1167 return StorageResultCode.OperationFailedStorageBusy;
San Mehat207e5382010-02-04 20:46:54 -08001168 } else {
San Mehatb1043402010-02-05 08:26:50 -08001169 return StorageResultCode.OperationFailedInternalError;
San Mehat207e5382010-02-04 20:46:54 -08001170 }
1171 }
1172 }
1173
1174 private int doFormatVolume(String path) {
1175 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001176 mConnector.execute("volume", "format", path);
San Mehatb1043402010-02-05 08:26:50 -08001177 return StorageResultCode.OperationSucceeded;
San Mehat207e5382010-02-04 20:46:54 -08001178 } catch (NativeDaemonConnectorException e) {
1179 int code = e.getCode();
1180 if (code == VoldResponseCode.OpFailedNoMedia) {
San Mehatb1043402010-02-05 08:26:50 -08001181 return StorageResultCode.OperationFailedNoMedia;
San Mehat207e5382010-02-04 20:46:54 -08001182 } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
San Mehatb1043402010-02-05 08:26:50 -08001183 return StorageResultCode.OperationFailedMediaCorrupt;
San Mehat207e5382010-02-04 20:46:54 -08001184 } else {
San Mehatb1043402010-02-05 08:26:50 -08001185 return StorageResultCode.OperationFailedInternalError;
San Mehat207e5382010-02-04 20:46:54 -08001186 }
1187 }
1188 }
1189
San Mehatb1043402010-02-05 08:26:50 -08001190 private boolean doGetVolumeShared(String path, String method) {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001191 final NativeDaemonEvent event;
Kenny Roota80ce062010-06-01 13:23:53 -07001192 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001193 event = mConnector.execute("volume", "shared", path, method);
Kenny Roota80ce062010-06-01 13:23:53 -07001194 } catch (NativeDaemonConnectorException ex) {
1195 Slog.e(TAG, "Failed to read response to volume shared " + path + " " + method);
1196 return false;
1197 }
San Mehatb1043402010-02-05 08:26:50 -08001198
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001199 if (event.getCode() == VoldResponseCode.ShareEnabledResult) {
1200 return event.getMessage().endsWith("enabled");
1201 } else {
1202 return false;
San Mehatb1043402010-02-05 08:26:50 -08001203 }
San Mehatb1043402010-02-05 08:26:50 -08001204 }
1205
Mike Lockwoodecedfdc2011-06-08 15:11:59 -07001206 private void notifyShareAvailabilityChange(final boolean avail) {
San Mehat4270e1e2010-01-29 05:32:19 -08001207 synchronized (mListeners) {
Mike Lockwoodecedfdc2011-06-08 15:11:59 -07001208 mUmsAvailable = avail;
San Mehat4270e1e2010-01-29 05:32:19 -08001209 for (int i = mListeners.size() -1; i >= 0; i--) {
1210 MountServiceBinderListener bl = mListeners.get(i);
1211 try {
San Mehatb1043402010-02-05 08:26:50 -08001212 bl.mListener.onUsbMassStorageConnectionChanged(avail);
San Mehat4270e1e2010-01-29 05:32:19 -08001213 } catch (RemoteException rex) {
San Mehata5078592010-03-25 09:36:54 -07001214 Slog.e(TAG, "Listener dead");
San Mehat4270e1e2010-01-29 05:32:19 -08001215 mListeners.remove(i);
1216 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -07001217 Slog.e(TAG, "Listener failed", ex);
San Mehat4270e1e2010-01-29 05:32:19 -08001218 }
1219 }
1220 }
1221
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001222 if (mSystemReady == true) {
San Mehat6a965af22010-02-24 17:47:30 -08001223 sendUmsIntent(avail);
1224 } else {
1225 mSendUmsConnectedOnBoot = avail;
San Mehat4270e1e2010-01-29 05:32:19 -08001226 }
San Mehat2fe718a2010-03-11 12:01:49 -08001227
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001228 final StorageVolume primary = getPrimaryPhysicalVolume();
1229 if (avail == false && primary != null
1230 && Environment.MEDIA_SHARED.equals(getVolumeState(primary.getPath()))) {
1231 final String path = primary.getPath();
San Mehat2fe718a2010-03-11 12:01:49 -08001232 /*
1233 * USB mass storage disconnected while enabled
1234 */
Dianne Hackborn8d044e82013-04-30 17:24:15 -07001235 new Thread("MountService#AvailabilityChange") {
Jason parks5af0b912010-11-29 09:05:25 -06001236 @Override
San Mehat2fe718a2010-03-11 12:01:49 -08001237 public void run() {
1238 try {
1239 int rc;
San Mehata5078592010-03-25 09:36:54 -07001240 Slog.w(TAG, "Disabling UMS after cable disconnect");
San Mehat2fe718a2010-03-11 12:01:49 -08001241 doShareUnshareVolume(path, "ums", false);
1242 if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {
San Mehata5078592010-03-25 09:36:54 -07001243 Slog.e(TAG, String.format(
San Mehat2fe718a2010-03-11 12:01:49 -08001244 "Failed to remount {%s} on UMS enabled-disconnect (%d)",
1245 path, rc));
1246 }
1247 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -07001248 Slog.w(TAG, "Failed to mount media on UMS enabled-disconnect", ex);
San Mehat2fe718a2010-03-11 12:01:49 -08001249 }
1250 }
1251 }.start();
1252 }
San Mehat4270e1e2010-01-29 05:32:19 -08001253 }
1254
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001255 private void sendStorageIntent(String action, StorageVolume volume, UserHandle user) {
1256 final Intent intent = new Intent(action, Uri.parse("file://" + volume.getPath()));
1257 intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, volume);
Johan Redestig0464c072014-01-18 22:46:56 +01001258 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001259 Slog.d(TAG, "sendStorageIntent " + intent + " to " + user);
1260 mContext.sendBroadcastAsUser(intent, user);
Mike Lockwooda5250c92011-05-23 13:44:04 -04001261 }
1262
San Mehat6a965af22010-02-24 17:47:30 -08001263 private void sendUmsIntent(boolean c) {
Dianne Hackborn5ac72a22012-08-29 18:32:08 -07001264 mContext.sendBroadcastAsUser(
1265 new Intent((c ? Intent.ACTION_UMS_CONNECTED : Intent.ACTION_UMS_DISCONNECTED)),
1266 UserHandle.ALL);
San Mehat6a965af22010-02-24 17:47:30 -08001267 }
1268
San Mehat207e5382010-02-04 20:46:54 -08001269 private void validatePermission(String perm) {
San Mehat4270e1e2010-01-29 05:32:19 -08001270 if (mContext.checkCallingOrSelfPermission(perm) != PackageManager.PERMISSION_GRANTED) {
1271 throw new SecurityException(String.format("Requires %s permission", perm));
1272 }
1273 }
1274
Emily Bernier92aa5a22014-07-07 10:11:48 -04001275 private boolean hasUserRestriction(String restriction) {
1276 UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
1277 return um.hasUserRestriction(restriction, Binder.getCallingUserHandle());
1278 }
1279
1280 private void validateUserRestriction(String restriction) {
1281 if (hasUserRestriction(restriction)) {
1282 throw new SecurityException("User has restriction " + restriction);
1283 }
1284 }
1285
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001286 // Storage list XML tags
1287 private static final String TAG_STORAGE_LIST = "StorageList";
1288 private static final String TAG_STORAGE = "storage";
1289
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001290 private void readStorageListLocked() {
1291 mVolumes.clear();
1292 mVolumeStates.clear();
1293
Fabrice Di Meglio13fe2a52012-05-18 18:08:58 -07001294 Resources resources = mContext.getResources();
1295
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001296 int id = com.android.internal.R.xml.storage_list;
1297 XmlResourceParser parser = resources.getXml(id);
1298 AttributeSet attrs = Xml.asAttributeSet(parser);
1299
1300 try {
1301 XmlUtils.beginDocument(parser, TAG_STORAGE_LIST);
1302 while (true) {
1303 XmlUtils.nextElement(parser);
1304
1305 String element = parser.getName();
1306 if (element == null) break;
1307
1308 if (TAG_STORAGE.equals(element)) {
1309 TypedArray a = resources.obtainAttributes(attrs,
1310 com.android.internal.R.styleable.Storage);
1311
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001312 String path = a.getString(
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001313 com.android.internal.R.styleable.Storage_mountPoint);
Fabrice Di Meglio13fe2a52012-05-18 18:08:58 -07001314 int descriptionId = a.getResourceId(
1315 com.android.internal.R.styleable.Storage_storageDescription, -1);
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001316 CharSequence description = a.getText(
1317 com.android.internal.R.styleable.Storage_storageDescription);
1318 boolean primary = a.getBoolean(
1319 com.android.internal.R.styleable.Storage_primary, false);
1320 boolean removable = a.getBoolean(
1321 com.android.internal.R.styleable.Storage_removable, false);
1322 boolean emulated = a.getBoolean(
1323 com.android.internal.R.styleable.Storage_emulated, false);
1324 int mtpReserve = a.getInt(
1325 com.android.internal.R.styleable.Storage_mtpReserve, 0);
Mike Lockwood8e8b2802011-06-07 08:03:33 -07001326 boolean allowMassStorage = a.getBoolean(
1327 com.android.internal.R.styleable.Storage_allowMassStorage, false);
Mike Lockwood7a59dd22011-07-11 09:18:03 -04001328 // resource parser does not support longs, so XML value is in megabytes
1329 long maxFileSize = a.getInt(
1330 com.android.internal.R.styleable.Storage_maxFileSize, 0) * 1024L * 1024L;
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001331
1332 Slog.d(TAG, "got storage path: " + path + " description: " + description +
1333 " primary: " + primary + " removable: " + removable +
Mike Lockwood8e8b2802011-06-07 08:03:33 -07001334 " emulated: " + emulated + " mtpReserve: " + mtpReserve +
Mike Lockwood7a59dd22011-07-11 09:18:03 -04001335 " allowMassStorage: " + allowMassStorage +
1336 " maxFileSize: " + maxFileSize);
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001337
1338 if (emulated) {
1339 // For devices with emulated storage, we create separate
1340 // volumes for each known user.
1341 mEmulatedTemplate = new StorageVolume(null, descriptionId, true, false,
1342 true, mtpReserve, false, maxFileSize, null);
1343
1344 final UserManagerService userManager = UserManagerService.getInstance();
Amith Yamasani920ace02012-09-20 22:15:37 -07001345 for (UserInfo user : userManager.getUsers(false)) {
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001346 createEmulatedVolumeForUserLocked(user.getUserHandle());
1347 }
1348
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001349 } else {
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001350 if (path == null || description == null) {
1351 Slog.e(TAG, "Missing storage path or description in readStorageList");
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001352 } else {
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001353 final StorageVolume volume = new StorageVolume(new File(path),
1354 descriptionId, primary, removable, emulated, mtpReserve,
1355 allowMassStorage, maxFileSize, null);
1356 addVolumeLocked(volume);
Jeff Sharkey44cbdec2013-10-07 16:49:47 -07001357
1358 // Until we hear otherwise, treat as unmounted
1359 mVolumeStates.put(volume.getPath(), Environment.MEDIA_UNMOUNTED);
Jeff Sharkey1f706c62013-10-17 10:52:17 -07001360 volume.setState(Environment.MEDIA_UNMOUNTED);
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001361 }
1362 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001363
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001364 a.recycle();
1365 }
1366 }
1367 } catch (XmlPullParserException e) {
1368 throw new RuntimeException(e);
1369 } catch (IOException e) {
1370 throw new RuntimeException(e);
1371 } finally {
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001372 // Compute storage ID for each physical volume; emulated storage is
1373 // always 0 when defined.
1374 int index = isExternalStorageEmulated() ? 1 : 0;
1375 for (StorageVolume volume : mVolumes) {
1376 if (!volume.isEmulated()) {
1377 volume.setStorageId(index++);
1378 }
Mike Lockwoodfbfe5552011-05-17 17:19:37 -04001379 }
Mike Lockwood2f6a3882011-05-09 19:08:06 -07001380 parser.close();
1381 }
1382 }
1383
San Mehat4270e1e2010-01-29 05:32:19 -08001384 /**
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001385 * Create and add new {@link StorageVolume} for given {@link UserHandle}
1386 * using {@link #mEmulatedTemplate} as template.
1387 */
1388 private void createEmulatedVolumeForUserLocked(UserHandle user) {
1389 if (mEmulatedTemplate == null) {
1390 throw new IllegalStateException("Missing emulated volume multi-user template");
1391 }
1392
1393 final UserEnvironment userEnv = new UserEnvironment(user.getIdentifier());
1394 final File path = userEnv.getExternalStorageDirectory();
1395 final StorageVolume volume = StorageVolume.fromTemplate(mEmulatedTemplate, path, user);
1396 volume.setStorageId(0);
1397 addVolumeLocked(volume);
1398
1399 if (mSystemReady) {
1400 updatePublicVolumeState(volume, Environment.MEDIA_MOUNTED);
1401 } else {
1402 // Place stub status for early callers to find
1403 mVolumeStates.put(volume.getPath(), Environment.MEDIA_MOUNTED);
Jeff Sharkey1f706c62013-10-17 10:52:17 -07001404 volume.setState(Environment.MEDIA_MOUNTED);
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001405 }
1406 }
1407
1408 private void addVolumeLocked(StorageVolume volume) {
1409 Slog.d(TAG, "addVolumeLocked() " + volume);
1410 mVolumes.add(volume);
1411 final StorageVolume existing = mVolumesByPath.put(volume.getPath(), volume);
1412 if (existing != null) {
1413 throw new IllegalStateException(
1414 "Volume at " + volume.getPath() + " already exists: " + existing);
1415 }
1416 }
1417
1418 private void removeVolumeLocked(StorageVolume volume) {
1419 Slog.d(TAG, "removeVolumeLocked() " + volume);
1420 mVolumes.remove(volume);
1421 mVolumesByPath.remove(volume.getPath());
1422 mVolumeStates.remove(volume.getPath());
1423 }
1424
1425 private StorageVolume getPrimaryPhysicalVolume() {
1426 synchronized (mVolumesLock) {
1427 for (StorageVolume volume : mVolumes) {
1428 if (volume.isPrimary() && !volume.isEmulated()) {
1429 return volume;
1430 }
1431 }
1432 }
1433 return null;
1434 }
1435
1436 /**
San Mehat207e5382010-02-04 20:46:54 -08001437 * Constructs a new MountService instance
1438 *
1439 * @param context Binder context for this service
1440 */
1441 public MountService(Context context) {
Christopher Tated417d622013-08-19 16:14:25 -07001442 sSelf = this;
1443
San Mehat207e5382010-02-04 20:46:54 -08001444 mContext = context;
1445
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001446 synchronized (mVolumesLock) {
1447 readStorageListLocked();
Mike Lockwood03559752010-07-19 18:25:03 -04001448 }
1449
San Mehat207e5382010-02-04 20:46:54 -08001450 // XXX: This will go away soon in favor of IMountServiceObserver
1451 mPms = (PackageManagerService) ServiceManager.getService("package");
1452
Dianne Hackbornefa92b22013-05-03 14:11:43 -07001453 HandlerThread hthread = new HandlerThread(TAG);
1454 hthread.start();
1455 mHandler = new MountServiceHandler(hthread.getLooper());
Daniel Sandler5f27ef42010-03-16 15:42:02 -04001456
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001457 // Watch for user changes
1458 final IntentFilter userFilter = new IntentFilter();
1459 userFilter.addAction(Intent.ACTION_USER_ADDED);
1460 userFilter.addAction(Intent.ACTION_USER_REMOVED);
1461 mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);
1462
1463 // Watch for USB changes on primary volume
1464 final StorageVolume primary = getPrimaryPhysicalVolume();
1465 if (primary != null && primary.allowMassStorage()) {
1466 mContext.registerReceiver(
1467 mUsbReceiver, new IntentFilter(UsbManager.ACTION_USB_STATE), null, mHandler);
1468 }
1469
Kenny Roota02b8b02010-08-05 16:14:17 -07001470 // Add OBB Action Handler to MountService thread.
Dianne Hackborn8d044e82013-04-30 17:24:15 -07001471 mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper());
Kenny Roota02b8b02010-08-05 16:14:17 -07001472
Marco Nelissenc34ebce2010-02-18 13:39:41 -08001473 /*
Kenny Root305bcbf2010-09-03 07:56:38 -07001474 * Create the connection to vold with a maximum queue of twice the
1475 * amount of containers we'd ever expect to have. This keeps an
1476 * "asec list" from blocking a thread repeatedly.
1477 */
Dianne Hackborn77b987f2014-02-26 16:20:52 -08001478 mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25,
1479 null);
Kenny Root51a573c2012-05-17 13:30:28 -07001480
Kenny Root305bcbf2010-09-03 07:56:38 -07001481 Thread thread = new Thread(mConnector, VOLD_TAG);
San Mehat207e5382010-02-04 20:46:54 -08001482 thread.start();
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -07001483
Kenny Root07714d42011-08-17 17:49:28 -07001484 // Add ourself to the Watchdog monitors if enabled.
1485 if (WATCHDOG_ENABLE) {
1486 Watchdog.getInstance().addMonitor(this);
1487 }
San Mehat207e5382010-02-04 20:46:54 -08001488 }
1489
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001490 public void systemReady() {
1491 mSystemReady = true;
1492 mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget();
1493 }
1494
San Mehat207e5382010-02-04 20:46:54 -08001495 /**
San Mehat4270e1e2010-01-29 05:32:19 -08001496 * Exposed API calls below here
1497 */
1498
1499 public void registerListener(IMountServiceListener listener) {
1500 synchronized (mListeners) {
1501 MountServiceBinderListener bl = new MountServiceBinderListener(listener);
1502 try {
1503 listener.asBinder().linkToDeath(bl, 0);
1504 mListeners.add(bl);
1505 } catch (RemoteException rex) {
San Mehata5078592010-03-25 09:36:54 -07001506 Slog.e(TAG, "Failed to link to listener death");
San Mehat4270e1e2010-01-29 05:32:19 -08001507 }
1508 }
1509 }
1510
1511 public void unregisterListener(IMountServiceListener listener) {
1512 synchronized (mListeners) {
1513 for(MountServiceBinderListener bl : mListeners) {
Niklas Brunlidd64fe0f2013-07-05 08:54:03 +02001514 if (bl.mListener.asBinder() == listener.asBinder()) {
San Mehat4270e1e2010-01-29 05:32:19 -08001515 mListeners.remove(mListeners.indexOf(bl));
Vairavan Srinivasan5c25a2d2012-01-24 08:22:14 -08001516 listener.asBinder().unlinkToDeath(bl, 0);
San Mehat4270e1e2010-01-29 05:32:19 -08001517 return;
1518 }
1519 }
1520 }
1521 }
1522
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -08001523 public void shutdown(final IMountShutdownObserver observer) {
San Mehat4270e1e2010-01-29 05:32:19 -08001524 validatePermission(android.Manifest.permission.SHUTDOWN);
1525
San Mehata5078592010-03-25 09:36:54 -07001526 Slog.i(TAG, "Shutting down");
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001527 synchronized (mVolumesLock) {
Rickard Helldin5fb5df02014-02-07 09:35:04 +01001528 // Get all volumes to be unmounted.
1529 MountShutdownLatch mountShutdownLatch = new MountShutdownLatch(observer,
1530 mVolumeStates.size());
1531
Mike Lockwood7fa24aa2011-03-23 14:52:34 -04001532 for (String path : mVolumeStates.keySet()) {
1533 String state = mVolumeStates.get(path);
San Mehat4270e1e2010-01-29 05:32:19 -08001534
Mike Lockwood7fa24aa2011-03-23 14:52:34 -04001535 if (state.equals(Environment.MEDIA_SHARED)) {
1536 /*
1537 * If the media is currently shared, unshare it.
1538 * XXX: This is still dangerous!. We should not
1539 * be rebooting at *all* if UMS is enabled, since
1540 * the UMS host could have dirty FAT cache entries
1541 * yet to flush.
1542 */
1543 setUsbMassStorageEnabled(false);
1544 } else if (state.equals(Environment.MEDIA_CHECKING)) {
1545 /*
1546 * If the media is being checked, then we need to wait for
1547 * it to complete before being able to proceed.
1548 */
1549 // XXX: @hackbod - Should we disable the ANR timer here?
1550 int retries = 30;
1551 while (state.equals(Environment.MEDIA_CHECKING) && (retries-- >=0)) {
1552 try {
1553 Thread.sleep(1000);
1554 } catch (InterruptedException iex) {
1555 Slog.e(TAG, "Interrupted while waiting for media", iex);
1556 break;
1557 }
1558 state = Environment.getExternalStorageState();
1559 }
1560 if (retries == 0) {
1561 Slog.e(TAG, "Timed out waiting for media to check");
1562 }
San Mehat91c77612010-01-07 10:39:41 -08001563 }
San Mehat91c77612010-01-07 10:39:41 -08001564
Mike Lockwood7fa24aa2011-03-23 14:52:34 -04001565 if (state.equals(Environment.MEDIA_MOUNTED)) {
1566 // Post a unmount message.
Rickard Helldin5fb5df02014-02-07 09:35:04 +01001567 ShutdownCallBack ucb = new ShutdownCallBack(path, mountShutdownLatch);
Mike Lockwood7fa24aa2011-03-23 14:52:34 -04001568 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
1569 } else if (observer != null) {
1570 /*
Rickard Helldin5fb5df02014-02-07 09:35:04 +01001571 * Count down, since nothing will be done. The observer will be
1572 * notified when we are done so shutdown sequence can continue.
Mike Lockwood7fa24aa2011-03-23 14:52:34 -04001573 */
Rickard Helldin5fb5df02014-02-07 09:35:04 +01001574 mountShutdownLatch.countDown();
1575 Slog.i(TAG, "Unmount completed: " + path +
1576 ", result code: " + StorageResultCode.OperationSucceeded);
Mike Lockwood7fa24aa2011-03-23 14:52:34 -04001577 }
Johan Alfven5d0db4d2010-11-09 10:32:25 +01001578 }
San Mehat4270e1e2010-01-29 05:32:19 -08001579 }
1580 }
1581
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001582 private boolean getUmsEnabling() {
1583 synchronized (mListeners) {
1584 return mUmsEnabling;
1585 }
1586 }
1587
1588 private void setUmsEnabling(boolean enable) {
1589 synchronized (mListeners) {
Tony Wufc711252010-08-09 16:49:19 +08001590 mUmsEnabling = enable;
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001591 }
1592 }
1593
San Mehatb1043402010-02-05 08:26:50 -08001594 public boolean isUsbMassStorageConnected() {
San Mehat207e5382010-02-04 20:46:54 -08001595 waitForReady();
San Mehat91c77612010-01-07 10:39:41 -08001596
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001597 if (getUmsEnabling()) {
San Mehatb1043402010-02-05 08:26:50 -08001598 return true;
San Mehat7fd0fee2009-12-17 07:12:23 -08001599 }
Mike Lockwoodecedfdc2011-06-08 15:11:59 -07001600 synchronized (mListeners) {
1601 return mUmsAvailable;
1602 }
San Mehatb1043402010-02-05 08:26:50 -08001603 }
1604
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001605 public void setUsbMassStorageEnabled(boolean enable) {
San Mehatb1043402010-02-05 08:26:50 -08001606 waitForReady();
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001607 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
Emily Bernier92aa5a22014-07-07 10:11:48 -04001608 validateUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER);
San Mehatb1043402010-02-05 08:26:50 -08001609
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001610 final StorageVolume primary = getPrimaryPhysicalVolume();
1611 if (primary == null) return;
1612
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001613 // TODO: Add support for multiple share methods
1614
1615 /*
1616 * If the volume is mounted and we're enabling then unmount it
1617 */
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001618 String path = primary.getPath();
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001619 String vs = getVolumeState(path);
1620 String method = "ums";
1621 if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
1622 // Override for isUsbMassStorageEnabled()
1623 setUmsEnabling(enable);
1624 UmsEnableCallBack umscb = new UmsEnableCallBack(path, method, true);
1625 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, umscb));
1626 // Clear override
1627 setUmsEnabling(false);
1628 }
1629 /*
1630 * If we disabled UMS then mount the volume
1631 */
1632 if (!enable) {
1633 doShareUnshareVolume(path, method, enable);
1634 if (doMountVolume(path) != StorageResultCode.OperationSucceeded) {
San Mehata5078592010-03-25 09:36:54 -07001635 Slog.e(TAG, "Failed to remount " + path +
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001636 " after disabling share method " + method);
1637 /*
1638 * Even though the mount failed, the unshare didn't so don't indicate an error.
1639 * The mountVolume() call will have set the storage state and sent the necessary
1640 * broadcasts.
1641 */
1642 }
1643 }
San Mehatb1043402010-02-05 08:26:50 -08001644 }
1645
1646 public boolean isUsbMassStorageEnabled() {
1647 waitForReady();
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001648
1649 final StorageVolume primary = getPrimaryPhysicalVolume();
1650 if (primary != null) {
1651 return doGetVolumeShared(primary.getPath(), "ums");
1652 } else {
1653 return false;
1654 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001655 }
Jason parks9ed98bc2011-01-17 09:58:35 -06001656
San Mehat7fd0fee2009-12-17 07:12:23 -08001657 /**
1658 * @return state of the volume at the specified mount point
1659 */
San Mehat4270e1e2010-01-29 05:32:19 -08001660 public String getVolumeState(String mountPoint) {
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001661 synchronized (mVolumesLock) {
Mike Lockwood7fa24aa2011-03-23 14:52:34 -04001662 String state = mVolumeStates.get(mountPoint);
1663 if (state == null) {
1664 Slog.w(TAG, "getVolumeState(" + mountPoint + "): Unknown volume");
Ken Sumrall18db5c52011-07-14 11:35:06 -07001665 if (SystemProperties.get("vold.encrypt_progress").length() != 0) {
1666 state = Environment.MEDIA_REMOVED;
1667 } else {
1668 throw new IllegalArgumentException();
1669 }
Mike Lockwood7fa24aa2011-03-23 14:52:34 -04001670 }
San Mehat7fd0fee2009-12-17 07:12:23 -08001671
Mike Lockwood7fa24aa2011-03-23 14:52:34 -04001672 return state;
1673 }
San Mehat7fd0fee2009-12-17 07:12:23 -08001674 }
1675
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001676 @Override
Kenny Roote1ff2142010-10-12 11:20:01 -07001677 public boolean isExternalStorageEmulated() {
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001678 return mEmulatedTemplate != null;
Kenny Roote1ff2142010-10-12 11:20:01 -07001679 }
1680
San Mehat4270e1e2010-01-29 05:32:19 -08001681 public int mountVolume(String path) {
1682 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
San Mehat207e5382010-02-04 20:46:54 -08001683 waitForReady();
1684 return doMountVolume(path);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001685 }
1686
Ben Komalo13c71972011-09-07 16:35:56 -07001687 public void unmountVolume(String path, boolean force, boolean removeEncryption) {
San Mehat4270e1e2010-01-29 05:32:19 -08001688 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
San Mehat207e5382010-02-04 20:46:54 -08001689 waitForReady();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001690
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -08001691 String volState = getVolumeState(path);
Ben Komalo13c71972011-09-07 16:35:56 -07001692 if (DEBUG_UNMOUNT) {
1693 Slog.i(TAG, "Unmounting " + path
1694 + " force = " + force
1695 + " removeEncryption = " + removeEncryption);
1696 }
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -08001697 if (Environment.MEDIA_UNMOUNTED.equals(volState) ||
1698 Environment.MEDIA_REMOVED.equals(volState) ||
1699 Environment.MEDIA_SHARED.equals(volState) ||
1700 Environment.MEDIA_UNMOUNTABLE.equals(volState)) {
1701 // Media already unmounted or cannot be unmounted.
1702 // TODO return valid return code when adding observer call back.
1703 return;
1704 }
Ben Komalo13c71972011-09-07 16:35:56 -07001705 UnmountCallBack ucb = new UnmountCallBack(path, force, removeEncryption);
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -08001706 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001707 }
1708
San Mehat4270e1e2010-01-29 05:32:19 -08001709 public int formatVolume(String path) {
1710 validatePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
San Mehat207e5382010-02-04 20:46:54 -08001711 waitForReady();
San Mehat5b77dab2010-01-26 13:28:50 -08001712
San Mehat207e5382010-02-04 20:46:54 -08001713 return doFormatVolume(path);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001714 }
1715
Mike Lockwoodecedfdc2011-06-08 15:11:59 -07001716 public int[] getStorageUsers(String path) {
San Mehatc1b4ce92010-02-16 17:13:03 -08001717 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1718 waitForReady();
1719 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001720 final String[] r = NativeDaemonEvent.filterMessageList(
1721 mConnector.executeForList("storage", "users", path),
1722 VoldResponseCode.StorageUsersListResult);
1723
San Mehatc1b4ce92010-02-16 17:13:03 -08001724 // FMT: <pid> <process name>
1725 int[] data = new int[r.length];
1726 for (int i = 0; i < r.length; i++) {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001727 String[] tok = r[i].split(" ");
San Mehatc1b4ce92010-02-16 17:13:03 -08001728 try {
1729 data[i] = Integer.parseInt(tok[0]);
1730 } catch (NumberFormatException nfe) {
San Mehata5078592010-03-25 09:36:54 -07001731 Slog.e(TAG, String.format("Error parsing pid %s", tok[0]));
San Mehatc1b4ce92010-02-16 17:13:03 -08001732 return new int[0];
1733 }
1734 }
1735 return data;
1736 } catch (NativeDaemonConnectorException e) {
San Mehata5078592010-03-25 09:36:54 -07001737 Slog.e(TAG, "Failed to retrieve storage users list", e);
San Mehatc1b4ce92010-02-16 17:13:03 -08001738 return new int[0];
1739 }
1740 }
1741
San Mehatb1043402010-02-05 08:26:50 -08001742 private void warnOnNotMounted() {
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001743 final StorageVolume primary = getPrimaryPhysicalVolume();
Jeff Sharkey32ee8312012-09-30 13:21:31 -07001744 if (primary != null) {
1745 boolean mounted = false;
1746 try {
1747 mounted = Environment.MEDIA_MOUNTED.equals(getVolumeState(primary.getPath()));
Jeff Sharkey9ae62f52013-03-26 10:29:01 -07001748 } catch (IllegalArgumentException e) {
Jeff Sharkey32ee8312012-09-30 13:21:31 -07001749 }
1750
1751 if (!mounted) {
1752 Slog.w(TAG, "getSecureContainerList() called when storage not mounted");
1753 }
San Mehatb1043402010-02-05 08:26:50 -08001754 }
1755 }
1756
San Mehat4270e1e2010-01-29 05:32:19 -08001757 public String[] getSecureContainerList() {
1758 validatePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat207e5382010-02-04 20:46:54 -08001759 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001760 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001761
San Mehat4270e1e2010-01-29 05:32:19 -08001762 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001763 return NativeDaemonEvent.filterMessageList(
1764 mConnector.executeForList("asec", "list"), VoldResponseCode.AsecListResult);
San Mehat4270e1e2010-01-29 05:32:19 -08001765 } catch (NativeDaemonConnectorException e) {
1766 return new String[0];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001767 }
1768 }
San Mehat36972292010-01-06 11:06:32 -08001769
Kenny Root6dceb882012-04-12 14:23:49 -07001770 public int createSecureContainer(String id, int sizeMb, String fstype, String key,
1771 int ownerUid, boolean external) {
San Mehat4270e1e2010-01-29 05:32:19 -08001772 validatePermission(android.Manifest.permission.ASEC_CREATE);
San Mehat207e5382010-02-04 20:46:54 -08001773 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001774 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001775
San Mehatb1043402010-02-05 08:26:50 -08001776 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001777 try {
Jeff Sharkey56cd6462013-06-07 15:09:15 -07001778 mConnector.execute("asec", "create", id, sizeMb, fstype, new SensitiveArg(key),
1779 ownerUid, external ? "1" : "0");
San Mehat4270e1e2010-01-29 05:32:19 -08001780 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001781 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001782 }
San Mehata181b212010-02-11 06:50:20 -08001783
1784 if (rc == StorageResultCode.OperationSucceeded) {
1785 synchronized (mAsecMountSet) {
1786 mAsecMountSet.add(id);
1787 }
1788 }
San Mehat4270e1e2010-01-29 05:32:19 -08001789 return rc;
San Mehat36972292010-01-06 11:06:32 -08001790 }
1791
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001792 @Override
1793 public int resizeSecureContainer(String id, int sizeMb, String key) {
1794 validatePermission(android.Manifest.permission.ASEC_CREATE);
1795 waitForReady();
1796 warnOnNotMounted();
1797
1798 int rc = StorageResultCode.OperationSucceeded;
1799 try {
1800 mConnector.execute("asec", "resize", id, sizeMb, new SensitiveArg(key));
1801 } catch (NativeDaemonConnectorException e) {
1802 rc = StorageResultCode.OperationFailedInternalError;
1803 }
1804 return rc;
1805 }
1806
San Mehat4270e1e2010-01-29 05:32:19 -08001807 public int finalizeSecureContainer(String id) {
1808 validatePermission(android.Manifest.permission.ASEC_CREATE);
San Mehatb1043402010-02-05 08:26:50 -08001809 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001810
San Mehatb1043402010-02-05 08:26:50 -08001811 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001812 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001813 mConnector.execute("asec", "finalize", id);
San Mehata181b212010-02-11 06:50:20 -08001814 /*
1815 * Finalization does a remount, so no need
1816 * to update mAsecMountSet
1817 */
San Mehat4270e1e2010-01-29 05:32:19 -08001818 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001819 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001820 }
San Mehat4270e1e2010-01-29 05:32:19 -08001821 return rc;
San Mehat36972292010-01-06 11:06:32 -08001822 }
1823
Kenny Root6dceb882012-04-12 14:23:49 -07001824 public int fixPermissionsSecureContainer(String id, int gid, String filename) {
1825 validatePermission(android.Manifest.permission.ASEC_CREATE);
1826 warnOnNotMounted();
1827
1828 int rc = StorageResultCode.OperationSucceeded;
1829 try {
1830 mConnector.execute("asec", "fixperms", id, gid, filename);
1831 /*
1832 * Fix permissions does a remount, so no need to update
1833 * mAsecMountSet
1834 */
1835 } catch (NativeDaemonConnectorException e) {
1836 rc = StorageResultCode.OperationFailedInternalError;
1837 }
1838 return rc;
1839 }
1840
San Mehatd9709982010-02-18 11:43:03 -08001841 public int destroySecureContainer(String id, boolean force) {
San Mehat4270e1e2010-01-29 05:32:19 -08001842 validatePermission(android.Manifest.permission.ASEC_DESTROY);
San Mehat207e5382010-02-04 20:46:54 -08001843 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001844 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001845
Kenny Rootaa485402010-09-14 14:49:41 -07001846 /*
1847 * Force a GC to make sure AssetManagers in other threads of the
1848 * system_server are cleaned up. We have to do this since AssetManager
1849 * instances are kept as a WeakReference and it's possible we have files
1850 * open on the external storage.
1851 */
1852 Runtime.getRuntime().gc();
1853
San Mehatb1043402010-02-05 08:26:50 -08001854 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001855 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001856 final Command cmd = new Command("asec", "destroy", id);
1857 if (force) {
1858 cmd.appendArg("force");
1859 }
1860 mConnector.execute(cmd);
San Mehat4270e1e2010-01-29 05:32:19 -08001861 } catch (NativeDaemonConnectorException e) {
San Mehatd9709982010-02-18 11:43:03 -08001862 int code = e.getCode();
1863 if (code == VoldResponseCode.OpFailedStorageBusy) {
1864 rc = StorageResultCode.OperationFailedStorageBusy;
1865 } else {
1866 rc = StorageResultCode.OperationFailedInternalError;
1867 }
San Mehat02735bc2010-01-26 15:18:08 -08001868 }
San Mehata181b212010-02-11 06:50:20 -08001869
1870 if (rc == StorageResultCode.OperationSucceeded) {
1871 synchronized (mAsecMountSet) {
1872 if (mAsecMountSet.contains(id)) {
1873 mAsecMountSet.remove(id);
1874 }
1875 }
1876 }
1877
San Mehat4270e1e2010-01-29 05:32:19 -08001878 return rc;
San Mehat36972292010-01-06 11:06:32 -08001879 }
Jason parks9ed98bc2011-01-17 09:58:35 -06001880
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001881 public int mountSecureContainer(String id, String key, int ownerUid, boolean readOnly) {
San Mehat4270e1e2010-01-29 05:32:19 -08001882 validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
San Mehat207e5382010-02-04 20:46:54 -08001883 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001884 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001885
San Mehata181b212010-02-11 06:50:20 -08001886 synchronized (mAsecMountSet) {
1887 if (mAsecMountSet.contains(id)) {
1888 return StorageResultCode.OperationFailedStorageMounted;
1889 }
1890 }
1891
San Mehatb1043402010-02-05 08:26:50 -08001892 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001893 try {
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001894 mConnector.execute("asec", "mount", id, new SensitiveArg(key), ownerUid,
1895 readOnly ? "ro" : "rw");
San Mehat4270e1e2010-01-29 05:32:19 -08001896 } catch (NativeDaemonConnectorException e) {
Kenny Rootf0304622010-03-19 19:20:42 -07001897 int code = e.getCode();
1898 if (code != VoldResponseCode.OpFailedStorageBusy) {
1899 rc = StorageResultCode.OperationFailedInternalError;
1900 }
San Mehat02735bc2010-01-26 15:18:08 -08001901 }
San Mehat6cdd9c02010-02-09 14:45:20 -08001902
1903 if (rc == StorageResultCode.OperationSucceeded) {
1904 synchronized (mAsecMountSet) {
1905 mAsecMountSet.add(id);
1906 }
1907 }
San Mehat4270e1e2010-01-29 05:32:19 -08001908 return rc;
San Mehat36972292010-01-06 11:06:32 -08001909 }
1910
San Mehatd9709982010-02-18 11:43:03 -08001911 public int unmountSecureContainer(String id, boolean force) {
San Mehat4270e1e2010-01-29 05:32:19 -08001912 validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
San Mehat207e5382010-02-04 20:46:54 -08001913 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001914 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001915
San Mehat6cdd9c02010-02-09 14:45:20 -08001916 synchronized (mAsecMountSet) {
1917 if (!mAsecMountSet.contains(id)) {
San Mehata181b212010-02-11 06:50:20 -08001918 return StorageResultCode.OperationFailedStorageNotMounted;
San Mehat6cdd9c02010-02-09 14:45:20 -08001919 }
1920 }
1921
Kenny Rootaa485402010-09-14 14:49:41 -07001922 /*
1923 * Force a GC to make sure AssetManagers in other threads of the
1924 * system_server are cleaned up. We have to do this since AssetManager
1925 * instances are kept as a WeakReference and it's possible we have files
1926 * open on the external storage.
1927 */
1928 Runtime.getRuntime().gc();
1929
San Mehatb1043402010-02-05 08:26:50 -08001930 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001931 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001932 final Command cmd = new Command("asec", "unmount", id);
1933 if (force) {
1934 cmd.appendArg("force");
1935 }
1936 mConnector.execute(cmd);
San Mehat4270e1e2010-01-29 05:32:19 -08001937 } catch (NativeDaemonConnectorException e) {
San Mehatd9709982010-02-18 11:43:03 -08001938 int code = e.getCode();
1939 if (code == VoldResponseCode.OpFailedStorageBusy) {
1940 rc = StorageResultCode.OperationFailedStorageBusy;
1941 } else {
1942 rc = StorageResultCode.OperationFailedInternalError;
1943 }
San Mehat02735bc2010-01-26 15:18:08 -08001944 }
San Mehat6cdd9c02010-02-09 14:45:20 -08001945
1946 if (rc == StorageResultCode.OperationSucceeded) {
1947 synchronized (mAsecMountSet) {
1948 mAsecMountSet.remove(id);
1949 }
1950 }
San Mehat4270e1e2010-01-29 05:32:19 -08001951 return rc;
San Mehat9dba7092010-01-18 06:47:41 -08001952 }
1953
San Mehat6cdd9c02010-02-09 14:45:20 -08001954 public boolean isSecureContainerMounted(String id) {
1955 validatePermission(android.Manifest.permission.ASEC_ACCESS);
1956 waitForReady();
1957 warnOnNotMounted();
1958
1959 synchronized (mAsecMountSet) {
1960 return mAsecMountSet.contains(id);
1961 }
1962 }
1963
San Mehat4270e1e2010-01-29 05:32:19 -08001964 public int renameSecureContainer(String oldId, String newId) {
1965 validatePermission(android.Manifest.permission.ASEC_RENAME);
San Mehat207e5382010-02-04 20:46:54 -08001966 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001967 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001968
San Mehata181b212010-02-11 06:50:20 -08001969 synchronized (mAsecMountSet) {
San Mehat85451ee2010-02-24 08:54:18 -08001970 /*
Jason parks9ed98bc2011-01-17 09:58:35 -06001971 * Because a mounted container has active internal state which cannot be
San Mehat85451ee2010-02-24 08:54:18 -08001972 * changed while active, we must ensure both ids are not currently mounted.
1973 */
1974 if (mAsecMountSet.contains(oldId) || mAsecMountSet.contains(newId)) {
San Mehata181b212010-02-11 06:50:20 -08001975 return StorageResultCode.OperationFailedStorageMounted;
1976 }
1977 }
1978
San Mehatb1043402010-02-05 08:26:50 -08001979 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001980 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001981 mConnector.execute("asec", "rename", oldId, newId);
San Mehat4270e1e2010-01-29 05:32:19 -08001982 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001983 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001984 }
San Mehata181b212010-02-11 06:50:20 -08001985
San Mehat4270e1e2010-01-29 05:32:19 -08001986 return rc;
San Mehat45f61042010-01-23 08:12:43 -08001987 }
1988
San Mehat4270e1e2010-01-29 05:32:19 -08001989 public String getSecureContainerPath(String id) {
1990 validatePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat207e5382010-02-04 20:46:54 -08001991 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001992 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001993
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001994 final NativeDaemonEvent event;
San Mehat2d66cef2010-03-23 11:12:52 -07001995 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001996 event = mConnector.execute("asec", "path", id);
1997 event.checkCode(VoldResponseCode.AsecPathResult);
1998 return event.getMessage();
San Mehat2d66cef2010-03-23 11:12:52 -07001999 } catch (NativeDaemonConnectorException e) {
2000 int code = e.getCode();
2001 if (code == VoldResponseCode.OpFailedStorageNotFound) {
Fredrik Helmera20c8ef2011-02-09 16:16:10 +01002002 Slog.i(TAG, String.format("Container '%s' not found", id));
2003 return null;
San Mehat22dd86e2010-01-12 12:21:18 -08002004 } else {
San Mehat2d66cef2010-03-23 11:12:52 -07002005 throw new IllegalStateException(String.format("Unexpected response code %d", code));
San Mehat22dd86e2010-01-12 12:21:18 -08002006 }
2007 }
San Mehat22dd86e2010-01-12 12:21:18 -08002008 }
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -07002009
Dianne Hackborn292f8bc2011-06-27 16:27:41 -07002010 public String getSecureContainerFilesystemPath(String id) {
2011 validatePermission(android.Manifest.permission.ASEC_ACCESS);
2012 waitForReady();
2013 warnOnNotMounted();
2014
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002015 final NativeDaemonEvent event;
Dianne Hackborn292f8bc2011-06-27 16:27:41 -07002016 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002017 event = mConnector.execute("asec", "fspath", id);
2018 event.checkCode(VoldResponseCode.AsecPathResult);
2019 return event.getMessage();
Dianne Hackborn292f8bc2011-06-27 16:27:41 -07002020 } catch (NativeDaemonConnectorException e) {
2021 int code = e.getCode();
2022 if (code == VoldResponseCode.OpFailedStorageNotFound) {
2023 Slog.i(TAG, String.format("Container '%s' not found", id));
2024 return null;
2025 } else {
2026 throw new IllegalStateException(String.format("Unexpected response code %d", code));
2027 }
2028 }
2029 }
2030
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -07002031 public void finishMediaUpdate() {
2032 mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
2033 }
Kenny Root02c87302010-07-01 08:10:18 -07002034
Kenny Roota02b8b02010-08-05 16:14:17 -07002035 private boolean isUidOwnerOfPackageOrSystem(String packageName, int callerUid) {
2036 if (callerUid == android.os.Process.SYSTEM_UID) {
2037 return true;
2038 }
2039
Kenny Root02c87302010-07-01 08:10:18 -07002040 if (packageName == null) {
2041 return false;
2042 }
2043
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07002044 final int packageUid = mPms.getPackageUid(packageName, UserHandle.getUserId(callerUid));
Kenny Root02c87302010-07-01 08:10:18 -07002045
2046 if (DEBUG_OBB) {
2047 Slog.d(TAG, "packageName = " + packageName + ", packageUid = " +
2048 packageUid + ", callerUid = " + callerUid);
2049 }
2050
2051 return callerUid == packageUid;
2052 }
2053
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002054 public String getMountedObbPath(String rawPath) {
2055 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
Kenny Rootaf9d6672010-10-08 09:21:39 -07002056
Kenny Root02c87302010-07-01 08:10:18 -07002057 waitForReady();
2058 warnOnNotMounted();
2059
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002060 final ObbState state;
2061 synchronized (mObbPathToStateMap) {
2062 state = mObbPathToStateMap.get(rawPath);
2063 }
2064 if (state == null) {
2065 Slog.w(TAG, "Failed to find OBB mounted at " + rawPath);
2066 return null;
2067 }
2068
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002069 final NativeDaemonEvent event;
Kenny Root02c87302010-07-01 08:10:18 -07002070 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002071 event = mConnector.execute("obb", "path", state.voldPath);
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002072 event.checkCode(VoldResponseCode.AsecPathResult);
2073 return event.getMessage();
Kenny Root02c87302010-07-01 08:10:18 -07002074 } catch (NativeDaemonConnectorException e) {
2075 int code = e.getCode();
2076 if (code == VoldResponseCode.OpFailedStorageNotFound) {
Kenny Roota02b8b02010-08-05 16:14:17 -07002077 return null;
Kenny Root02c87302010-07-01 08:10:18 -07002078 } else {
2079 throw new IllegalStateException(String.format("Unexpected response code %d", code));
2080 }
2081 }
2082 }
2083
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002084 @Override
2085 public boolean isObbMounted(String rawPath) {
2086 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
Kenny Rootaf9d6672010-10-08 09:21:39 -07002087 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002088 return mObbPathToStateMap.containsKey(rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002089 }
Kenny Root02c87302010-07-01 08:10:18 -07002090 }
2091
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002092 @Override
2093 public void mountObb(
2094 String rawPath, String canonicalPath, String key, IObbActionListener token, int nonce) {
2095 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
2096 Preconditions.checkNotNull(canonicalPath, "canonicalPath cannot be null");
2097 Preconditions.checkNotNull(token, "token cannot be null");
Kenny Rootaf9d6672010-10-08 09:21:39 -07002098
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002099 final int callingUid = Binder.getCallingUid();
2100 final ObbState obbState = new ObbState(rawPath, canonicalPath, callingUid, token, nonce);
2101 final ObbAction action = new MountObbAction(obbState, key, callingUid);
Kenny Roota02b8b02010-08-05 16:14:17 -07002102 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
2103
2104 if (DEBUG_OBB)
2105 Slog.i(TAG, "Send to OBB handler: " + action.toString());
Kenny Root02c87302010-07-01 08:10:18 -07002106 }
2107
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002108 @Override
2109 public void unmountObb(String rawPath, boolean force, IObbActionListener token, int nonce) {
2110 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
2111
2112 final ObbState existingState;
2113 synchronized (mObbPathToStateMap) {
2114 existingState = mObbPathToStateMap.get(rawPath);
Kenny Rootf1121dc2010-09-29 07:30:53 -07002115 }
2116
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002117 if (existingState != null) {
2118 // TODO: separate state object from request data
2119 final int callingUid = Binder.getCallingUid();
2120 final ObbState newState = new ObbState(
2121 rawPath, existingState.canonicalPath, callingUid, token, nonce);
2122 final ObbAction action = new UnmountObbAction(newState, force);
2123 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
Kenny Root02c87302010-07-01 08:10:18 -07002124
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002125 if (DEBUG_OBB)
2126 Slog.i(TAG, "Send to OBB handler: " + action.toString());
2127 } else {
2128 Slog.w(TAG, "Unknown OBB mount at " + rawPath);
2129 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002130 }
2131
Ben Komalo444eca22011-09-01 15:17:44 -07002132 @Override
2133 public int getEncryptionState() {
2134 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2135 "no permission to access the crypt keeper");
2136
2137 waitForReady();
2138
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002139 final NativeDaemonEvent event;
Ben Komalo444eca22011-09-01 15:17:44 -07002140 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002141 event = mConnector.execute("cryptfs", "cryptocomplete");
2142 return Integer.parseInt(event.getMessage());
Ben Komalo444eca22011-09-01 15:17:44 -07002143 } catch (NumberFormatException e) {
2144 // Bad result - unexpected.
2145 Slog.w(TAG, "Unable to parse result from cryptfs cryptocomplete");
2146 return ENCRYPTION_STATE_ERROR_UNKNOWN;
2147 } catch (NativeDaemonConnectorException e) {
2148 // Something bad happened.
2149 Slog.w(TAG, "Error in communicating with cryptfs in validating");
2150 return ENCRYPTION_STATE_ERROR_UNKNOWN;
2151 }
2152 }
2153
Paul Lawrence8e397362014-01-27 15:22:30 -08002154 private String toHex(String password) {
2155 if (password == null) {
Paul Lawrence945490c2014-03-27 16:37:28 +00002156 return new String();
Paul Lawrence8e397362014-01-27 15:22:30 -08002157 }
2158 byte[] bytes = password.getBytes(StandardCharsets.UTF_8);
2159 return new String(Hex.encodeHex(bytes));
2160 }
2161
Paul Lawrence945490c2014-03-27 16:37:28 +00002162 private String fromHex(String hexPassword) {
2163 if (hexPassword == null) {
2164 return null;
2165 }
2166
2167 try {
2168 byte[] bytes = Hex.decodeHex(hexPassword.toCharArray());
2169 return new String(bytes, StandardCharsets.UTF_8);
2170 } catch (DecoderException e) {
2171 return null;
2172 }
2173 }
2174
Ben Komalo444eca22011-09-01 15:17:44 -07002175 @Override
Jason parks5af0b912010-11-29 09:05:25 -06002176 public int decryptStorage(String password) {
Jason parksf7b3cd42011-01-27 09:28:25 -06002177 if (TextUtils.isEmpty(password)) {
2178 throw new IllegalArgumentException("password cannot be empty");
Jason parks5af0b912010-11-29 09:05:25 -06002179 }
2180
Jason parks8888c592011-01-20 22:46:41 -06002181 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2182 "no permission to access the crypt keeper");
Jason parks5af0b912010-11-29 09:05:25 -06002183
2184 waitForReady();
2185
2186 if (DEBUG_EVENTS) {
2187 Slog.i(TAG, "decrypting storage...");
2188 }
2189
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002190 final NativeDaemonEvent event;
Jason parks5af0b912010-11-29 09:05:25 -06002191 try {
Paul Lawrence8e397362014-01-27 15:22:30 -08002192 event = mConnector.execute("cryptfs", "checkpw", new SensitiveArg(toHex(password)));
Jason parks9ed98bc2011-01-17 09:58:35 -06002193
Fredrik Roubertda6aedf2011-12-20 17:34:43 +01002194 final int code = Integer.parseInt(event.getMessage());
Jason parks9ed98bc2011-01-17 09:58:35 -06002195 if (code == 0) {
2196 // Decrypt was successful. Post a delayed message before restarting in order
2197 // to let the UI to clear itself
2198 mHandler.postDelayed(new Runnable() {
2199 public void run() {
Jeff Sharkey31c6e482011-11-18 17:09:01 -08002200 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002201 mConnector.execute("cryptfs", "restart");
Jeff Sharkey31c6e482011-11-18 17:09:01 -08002202 } catch (NativeDaemonConnectorException e) {
2203 Slog.e(TAG, "problem executing in background", e);
2204 }
Jason parks9ed98bc2011-01-17 09:58:35 -06002205 }
Jason parksf7b3cd42011-01-27 09:28:25 -06002206 }, 1000); // 1 second
Jason parks9ed98bc2011-01-17 09:58:35 -06002207 }
2208
2209 return code;
Jason parks5af0b912010-11-29 09:05:25 -06002210 } catch (NativeDaemonConnectorException e) {
2211 // Decryption failed
2212 return e.getCode();
2213 }
Jason parks5af0b912010-11-29 09:05:25 -06002214 }
2215
Paul Lawrence46791e72014-04-03 09:10:26 -07002216 public int encryptStorage(int type, String password) {
2217 if (TextUtils.isEmpty(password) && type != StorageManager.CRYPT_TYPE_DEFAULT) {
Jason parksf7b3cd42011-01-27 09:28:25 -06002218 throw new IllegalArgumentException("password cannot be empty");
Jason parks56aa5322011-01-07 09:01:15 -06002219 }
2220
Jason parks8888c592011-01-20 22:46:41 -06002221 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2222 "no permission to access the crypt keeper");
Jason parks56aa5322011-01-07 09:01:15 -06002223
2224 waitForReady();
2225
2226 if (DEBUG_EVENTS) {
Jason parks8888c592011-01-20 22:46:41 -06002227 Slog.i(TAG, "encrypting storage...");
Jason parks56aa5322011-01-07 09:01:15 -06002228 }
2229
2230 try {
Paul Lawrence46791e72014-04-03 09:10:26 -07002231 mConnector.execute("cryptfs", "enablecrypto", "inplace", CRYPTO_TYPES[type],
Paul Lawrence8e397362014-01-27 15:22:30 -08002232 new SensitiveArg(toHex(password)));
Jason parks56aa5322011-01-07 09:01:15 -06002233 } catch (NativeDaemonConnectorException e) {
2234 // Encryption failed
2235 return e.getCode();
2236 }
2237
2238 return 0;
2239 }
2240
Paul Lawrence8e397362014-01-27 15:22:30 -08002241 /** Set the password for encrypting the master key.
2242 * @param type One of the CRYPTO_TYPE_XXX consts defined in StorageManager.
2243 * @param password The password to set.
2244 */
2245 public int changeEncryptionPassword(int type, String password) {
Jason parksf7b3cd42011-01-27 09:28:25 -06002246 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2247 "no permission to access the crypt keeper");
2248
2249 waitForReady();
2250
2251 if (DEBUG_EVENTS) {
2252 Slog.i(TAG, "changing encryption password...");
2253 }
2254
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002255 final NativeDaemonEvent event;
Jason parksf7b3cd42011-01-27 09:28:25 -06002256 try {
Paul Lawrence8e397362014-01-27 15:22:30 -08002257 event = mConnector.execute("cryptfs", "changepw", CRYPTO_TYPES[type],
2258 new SensitiveArg(toHex(password)));
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002259 return Integer.parseInt(event.getMessage());
Jason parksf7b3cd42011-01-27 09:28:25 -06002260 } catch (NativeDaemonConnectorException e) {
2261 // Encryption failed
2262 return e.getCode();
2263 }
2264 }
2265
Christopher Tate32418be2011-10-10 13:51:12 -07002266 /**
2267 * Validate a user-supplied password string with cryptfs
2268 */
2269 @Override
2270 public int verifyEncryptionPassword(String password) throws RemoteException {
2271 // Only the system process is permitted to validate passwords
2272 if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
2273 throw new SecurityException("no permission to access the crypt keeper");
2274 }
2275
2276 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2277 "no permission to access the crypt keeper");
2278
2279 if (TextUtils.isEmpty(password)) {
2280 throw new IllegalArgumentException("password cannot be empty");
2281 }
2282
2283 waitForReady();
2284
2285 if (DEBUG_EVENTS) {
2286 Slog.i(TAG, "validating encryption password...");
2287 }
2288
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002289 final NativeDaemonEvent event;
Christopher Tate32418be2011-10-10 13:51:12 -07002290 try {
Paul Lawrence8e397362014-01-27 15:22:30 -08002291 event = mConnector.execute("cryptfs", "verifypw", new SensitiveArg(toHex(password)));
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002292 Slog.i(TAG, "cryptfs verifypw => " + event.getMessage());
2293 return Integer.parseInt(event.getMessage());
Christopher Tate32418be2011-10-10 13:51:12 -07002294 } catch (NativeDaemonConnectorException e) {
2295 // Encryption failed
2296 return e.getCode();
2297 }
2298 }
2299
Paul Lawrence8e397362014-01-27 15:22:30 -08002300 /**
2301 * Get the type of encryption used to encrypt the master key.
2302 * @return The type, one of the CRYPT_TYPE_XXX consts from StorageManager.
2303 */
2304 @Override
2305 public int getPasswordType() throws RemoteException {
2306
2307 waitForReady();
2308
2309 final NativeDaemonEvent event;
2310 try {
2311 event = mConnector.execute("cryptfs", "getpwtype");
2312 for (int i = 0; i < CRYPTO_TYPES.length; ++i) {
2313 if (CRYPTO_TYPES[i].equals(event.getMessage()))
2314 return i;
2315 }
2316
2317 throw new IllegalStateException("unexpected return from cryptfs");
2318 } catch (NativeDaemonConnectorException e) {
2319 throw e.rethrowAsParcelableException();
2320 }
2321 }
2322
Paul Lawrencee51dcf92014-03-18 10:56:00 -07002323 /**
2324 * Set a field in the crypto header.
2325 * @param field field to set
2326 * @param contents contents to set in field
2327 */
2328 @Override
2329 public void setField(String field, String contents) throws RemoteException {
2330
2331 waitForReady();
2332
2333 final NativeDaemonEvent event;
2334 try {
2335 event = mConnector.execute("cryptfs", "setfield", field, contents);
2336 } catch (NativeDaemonConnectorException e) {
2337 throw e.rethrowAsParcelableException();
2338 }
2339 }
2340
2341 /**
2342 * Gets a field from the crypto header.
2343 * @param field field to get
2344 * @return contents of field
2345 */
2346 @Override
2347 public String getField(String field) throws RemoteException {
2348
2349 waitForReady();
2350
2351 final NativeDaemonEvent event;
2352 try {
2353 final String[] contents = NativeDaemonEvent.filterMessageList(
2354 mConnector.executeForList("cryptfs", "getfield", field),
2355 VoldResponseCode.CryptfsGetfieldResult);
2356 String result = new String();
2357 for (String content : contents) {
2358 result += content;
2359 }
2360 return result;
2361 } catch (NativeDaemonConnectorException e) {
2362 throw e.rethrowAsParcelableException();
2363 }
2364 }
2365
Jeff Sharkeyb049e212012-09-07 23:16:01 -07002366 @Override
Paul Lawrence945490c2014-03-27 16:37:28 +00002367 public String getPassword() throws RemoteException {
2368 if (!isReady()) {
2369 return new String();
2370 }
2371
2372 final NativeDaemonEvent event;
2373 try {
2374 event = mConnector.execute("cryptfs", "getpw");
2375 return fromHex(event.getMessage());
2376 } catch (NativeDaemonConnectorException e) {
2377 throw e.rethrowAsParcelableException();
2378 }
2379 }
2380
2381 @Override
2382 public void clearPassword() throws RemoteException {
2383 if (!isReady()) {
2384 return;
2385 }
2386
2387 final NativeDaemonEvent event;
2388 try {
2389 event = mConnector.execute("cryptfs", "clearpw");
2390 } catch (NativeDaemonConnectorException e) {
2391 throw e.rethrowAsParcelableException();
2392 }
2393 }
2394
2395 @Override
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002396 public int mkdirs(String callingPkg, String appPath) {
2397 final int userId = UserHandle.getUserId(Binder.getCallingUid());
2398 final UserEnvironment userEnv = new UserEnvironment(userId);
2399
2400 // Validate that reported package name belongs to caller
2401 final AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(
2402 Context.APP_OPS_SERVICE);
2403 appOps.checkPackage(Binder.getCallingUid(), callingPkg);
2404
2405 try {
2406 appPath = new File(appPath).getCanonicalPath();
2407 } catch (IOException e) {
2408 Slog.e(TAG, "Failed to resolve " + appPath + ": " + e);
2409 return -1;
2410 }
2411
Jeff Sharkey5786a272013-10-02 12:50:34 -07002412 if (!appPath.endsWith("/")) {
2413 appPath = appPath + "/";
2414 }
2415
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002416 // Try translating the app path into a vold path, but require that it
2417 // belong to the calling package.
2418 String voldPath = maybeTranslatePathForVold(appPath,
2419 userEnv.buildExternalStorageAppDataDirs(callingPkg),
2420 userEnv.buildExternalStorageAppDataDirsForVold(callingPkg));
2421 if (voldPath != null) {
2422 try {
2423 mConnector.execute("volume", "mkdirs", voldPath);
2424 return 0;
2425 } catch (NativeDaemonConnectorException e) {
2426 return e.getCode();
2427 }
2428 }
2429
2430 voldPath = maybeTranslatePathForVold(appPath,
2431 userEnv.buildExternalStorageAppObbDirs(callingPkg),
2432 userEnv.buildExternalStorageAppObbDirsForVold(callingPkg));
2433 if (voldPath != null) {
2434 try {
2435 mConnector.execute("volume", "mkdirs", voldPath);
2436 return 0;
2437 } catch (NativeDaemonConnectorException e) {
2438 return e.getCode();
2439 }
2440 }
2441
Jeff Sharkey2ee3c1e2014-05-30 15:38:35 -07002442 voldPath = maybeTranslatePathForVold(appPath,
2443 userEnv.buildExternalStorageAppMediaDirs(callingPkg),
2444 userEnv.buildExternalStorageAppMediaDirsForVold(callingPkg));
2445 if (voldPath != null) {
2446 try {
2447 mConnector.execute("volume", "mkdirs", voldPath);
2448 return 0;
2449 } catch (NativeDaemonConnectorException e) {
2450 return e.getCode();
2451 }
2452 }
2453
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002454 throw new SecurityException("Invalid mkdirs path: " + appPath);
2455 }
2456
2457 /**
2458 * Translate the given path from an app-visible path to a vold-visible path,
2459 * but only if it's under the given whitelisted paths.
2460 *
2461 * @param path a canonicalized app-visible path.
2462 * @param appPaths list of app-visible paths that are allowed.
2463 * @param voldPaths list of vold-visible paths directly corresponding to the
2464 * allowed app-visible paths argument.
2465 * @return a vold-visible path representing the original path, or
2466 * {@code null} if the given path didn't have an app-to-vold
2467 * mapping.
2468 */
2469 @VisibleForTesting
2470 public static String maybeTranslatePathForVold(
2471 String path, File[] appPaths, File[] voldPaths) {
2472 if (appPaths.length != voldPaths.length) {
2473 throw new IllegalStateException("Paths must be 1:1 mapping");
2474 }
2475
2476 for (int i = 0; i < appPaths.length; i++) {
Jeff Sharkey5786a272013-10-02 12:50:34 -07002477 final String appPath = appPaths[i].getAbsolutePath() + "/";
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002478 if (path.startsWith(appPath)) {
Jeff Sharkey5786a272013-10-02 12:50:34 -07002479 path = new File(voldPaths[i], path.substring(appPath.length()))
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002480 .getAbsolutePath();
2481 if (!path.endsWith("/")) {
2482 path = path + "/";
2483 }
2484 return path;
2485 }
2486 }
2487 return null;
2488 }
2489
2490 @Override
Jeff Sharkeyb049e212012-09-07 23:16:01 -07002491 public StorageVolume[] getVolumeList() {
2492 final int callingUserId = UserHandle.getCallingUserId();
2493 final boolean accessAll = (mContext.checkPermission(
2494 android.Manifest.permission.ACCESS_ALL_EXTERNAL_STORAGE,
2495 Binder.getCallingPid(), Binder.getCallingUid()) == PERMISSION_GRANTED);
2496
2497 synchronized (mVolumesLock) {
2498 final ArrayList<StorageVolume> filtered = Lists.newArrayList();
2499 for (StorageVolume volume : mVolumes) {
2500 final UserHandle owner = volume.getOwner();
2501 final boolean ownerMatch = owner == null || owner.getIdentifier() == callingUserId;
2502 if (accessAll || ownerMatch) {
2503 filtered.add(volume);
2504 }
Mike Lockwood8fa5f802011-03-24 08:12:30 -07002505 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -07002506 return filtered.toArray(new StorageVolume[filtered.size()]);
Mike Lockwood8fa5f802011-03-24 08:12:30 -07002507 }
2508 }
2509
Kenny Rootaf9d6672010-10-08 09:21:39 -07002510 private void addObbStateLocked(ObbState obbState) throws RemoteException {
2511 final IBinder binder = obbState.getBinder();
2512 List<ObbState> obbStates = mObbMounts.get(binder);
Kenny Root5919ac62010-10-05 09:49:40 -07002513
Kenny Rootaf9d6672010-10-08 09:21:39 -07002514 if (obbStates == null) {
2515 obbStates = new ArrayList<ObbState>();
2516 mObbMounts.put(binder, obbStates);
2517 } else {
2518 for (final ObbState o : obbStates) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002519 if (o.rawPath.equals(obbState.rawPath)) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002520 throw new IllegalStateException("Attempt to add ObbState twice. "
2521 + "This indicates an error in the MountService logic.");
Kenny Root5919ac62010-10-05 09:49:40 -07002522 }
2523 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002524 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07002525
2526 obbStates.add(obbState);
2527 try {
2528 obbState.link();
2529 } catch (RemoteException e) {
2530 /*
2531 * The binder died before we could link it, so clean up our state
2532 * and return failure.
2533 */
2534 obbStates.remove(obbState);
2535 if (obbStates.isEmpty()) {
2536 mObbMounts.remove(binder);
2537 }
2538
2539 // Rethrow the error so mountObb can get it
2540 throw e;
2541 }
2542
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002543 mObbPathToStateMap.put(obbState.rawPath, obbState);
Kenny Roota02b8b02010-08-05 16:14:17 -07002544 }
2545
Kenny Rootaf9d6672010-10-08 09:21:39 -07002546 private void removeObbStateLocked(ObbState obbState) {
2547 final IBinder binder = obbState.getBinder();
2548 final List<ObbState> obbStates = mObbMounts.get(binder);
2549 if (obbStates != null) {
2550 if (obbStates.remove(obbState)) {
2551 obbState.unlink();
Kenny Root05105f72010-09-22 17:29:43 -07002552 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07002553 if (obbStates.isEmpty()) {
2554 mObbMounts.remove(binder);
2555 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002556 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002557
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002558 mObbPathToStateMap.remove(obbState.rawPath);
Kenny Root38cf8862010-09-26 14:18:51 -07002559 }
2560
Kenny Roota02b8b02010-08-05 16:14:17 -07002561 private class ObbActionHandler extends Handler {
2562 private boolean mBound = false;
Kenny Root480afe72010-10-07 10:17:50 -07002563 private final List<ObbAction> mActions = new LinkedList<ObbAction>();
Kenny Roota02b8b02010-08-05 16:14:17 -07002564
2565 ObbActionHandler(Looper l) {
2566 super(l);
2567 }
2568
2569 @Override
2570 public void handleMessage(Message msg) {
2571 switch (msg.what) {
2572 case OBB_RUN_ACTION: {
Kenny Root480afe72010-10-07 10:17:50 -07002573 final ObbAction action = (ObbAction) msg.obj;
Kenny Roota02b8b02010-08-05 16:14:17 -07002574
2575 if (DEBUG_OBB)
2576 Slog.i(TAG, "OBB_RUN_ACTION: " + action.toString());
2577
2578 // If a bind was already initiated we don't really
2579 // need to do anything. The pending install
2580 // will be processed later on.
2581 if (!mBound) {
2582 // If this is the only one pending we might
2583 // have to bind to the service again.
2584 if (!connectToService()) {
2585 Slog.e(TAG, "Failed to bind to media container service");
2586 action.handleError();
2587 return;
Kenny Roota02b8b02010-08-05 16:14:17 -07002588 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002589 }
Kenny Root735de3b2010-09-30 14:11:39 -07002590
Kenny Root735de3b2010-09-30 14:11:39 -07002591 mActions.add(action);
Kenny Roota02b8b02010-08-05 16:14:17 -07002592 break;
2593 }
2594 case OBB_MCS_BOUND: {
2595 if (DEBUG_OBB)
2596 Slog.i(TAG, "OBB_MCS_BOUND");
2597 if (msg.obj != null) {
2598 mContainerService = (IMediaContainerService) msg.obj;
2599 }
2600 if (mContainerService == null) {
2601 // Something seriously wrong. Bail out
2602 Slog.e(TAG, "Cannot bind to media container service");
2603 for (ObbAction action : mActions) {
2604 // Indicate service bind error
2605 action.handleError();
2606 }
2607 mActions.clear();
2608 } else if (mActions.size() > 0) {
Kenny Root480afe72010-10-07 10:17:50 -07002609 final ObbAction action = mActions.get(0);
Kenny Roota02b8b02010-08-05 16:14:17 -07002610 if (action != null) {
2611 action.execute(this);
2612 }
2613 } else {
2614 // Should never happen ideally.
2615 Slog.w(TAG, "Empty queue");
2616 }
2617 break;
2618 }
2619 case OBB_MCS_RECONNECT: {
2620 if (DEBUG_OBB)
2621 Slog.i(TAG, "OBB_MCS_RECONNECT");
2622 if (mActions.size() > 0) {
2623 if (mBound) {
2624 disconnectService();
2625 }
2626 if (!connectToService()) {
2627 Slog.e(TAG, "Failed to bind to media container service");
2628 for (ObbAction action : mActions) {
2629 // Indicate service bind error
2630 action.handleError();
2631 }
2632 mActions.clear();
2633 }
2634 }
2635 break;
2636 }
2637 case OBB_MCS_UNBIND: {
2638 if (DEBUG_OBB)
2639 Slog.i(TAG, "OBB_MCS_UNBIND");
2640
2641 // Delete pending install
2642 if (mActions.size() > 0) {
2643 mActions.remove(0);
2644 }
2645 if (mActions.size() == 0) {
2646 if (mBound) {
2647 disconnectService();
2648 }
2649 } else {
2650 // There are more pending requests in queue.
2651 // Just post MCS_BOUND message to trigger processing
2652 // of next pending install.
2653 mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND);
2654 }
2655 break;
2656 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07002657 case OBB_FLUSH_MOUNT_STATE: {
2658 final String path = (String) msg.obj;
2659
2660 if (DEBUG_OBB)
2661 Slog.i(TAG, "Flushing all OBB state for path " + path);
2662
2663 synchronized (mObbMounts) {
2664 final List<ObbState> obbStatesToRemove = new LinkedList<ObbState>();
2665
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002666 final Iterator<ObbState> i = mObbPathToStateMap.values().iterator();
Kenny Rootaf9d6672010-10-08 09:21:39 -07002667 while (i.hasNext()) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002668 final ObbState state = i.next();
Kenny Rootaf9d6672010-10-08 09:21:39 -07002669
2670 /*
2671 * If this entry's source file is in the volume path
2672 * that got unmounted, remove it because it's no
2673 * longer valid.
2674 */
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002675 if (state.canonicalPath.startsWith(path)) {
2676 obbStatesToRemove.add(state);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002677 }
2678 }
2679
2680 for (final ObbState obbState : obbStatesToRemove) {
2681 if (DEBUG_OBB)
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002682 Slog.i(TAG, "Removing state for " + obbState.rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002683
2684 removeObbStateLocked(obbState);
2685
2686 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002687 obbState.token.onObbResult(obbState.rawPath, obbState.nonce,
Kenny Rootaf9d6672010-10-08 09:21:39 -07002688 OnObbStateChangeListener.UNMOUNTED);
2689 } catch (RemoteException e) {
2690 Slog.i(TAG, "Couldn't send unmount notification for OBB: "
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002691 + obbState.rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002692 }
2693 }
2694 }
2695 break;
2696 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002697 }
2698 }
2699
2700 private boolean connectToService() {
2701 if (DEBUG_OBB)
2702 Slog.i(TAG, "Trying to bind to DefaultContainerService");
2703
2704 Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
2705 if (mContext.bindService(service, mDefContainerConn, Context.BIND_AUTO_CREATE)) {
2706 mBound = true;
2707 return true;
2708 }
2709 return false;
2710 }
2711
2712 private void disconnectService() {
2713 mContainerService = null;
2714 mBound = false;
2715 mContext.unbindService(mDefContainerConn);
2716 }
2717 }
2718
2719 abstract class ObbAction {
2720 private static final int MAX_RETRIES = 3;
2721 private int mRetries;
2722
2723 ObbState mObbState;
2724
2725 ObbAction(ObbState obbState) {
2726 mObbState = obbState;
2727 }
2728
2729 public void execute(ObbActionHandler handler) {
2730 try {
2731 if (DEBUG_OBB)
Ben Komalo444eca22011-09-01 15:17:44 -07002732 Slog.i(TAG, "Starting to execute action: " + toString());
Kenny Roota02b8b02010-08-05 16:14:17 -07002733 mRetries++;
2734 if (mRetries > MAX_RETRIES) {
2735 Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
Kenny Root480afe72010-10-07 10:17:50 -07002736 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
Kenny Roota02b8b02010-08-05 16:14:17 -07002737 handleError();
2738 return;
2739 } else {
2740 handleExecute();
2741 if (DEBUG_OBB)
2742 Slog.i(TAG, "Posting install MCS_UNBIND");
2743 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
2744 }
2745 } catch (RemoteException e) {
2746 if (DEBUG_OBB)
2747 Slog.i(TAG, "Posting install MCS_RECONNECT");
2748 mObbActionHandler.sendEmptyMessage(OBB_MCS_RECONNECT);
2749 } catch (Exception e) {
2750 if (DEBUG_OBB)
2751 Slog.d(TAG, "Error handling OBB action", e);
2752 handleError();
Kenny Root17eb6fb2010-10-06 15:02:52 -07002753 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
Kenny Roota02b8b02010-08-05 16:14:17 -07002754 }
2755 }
2756
Kenny Root05105f72010-09-22 17:29:43 -07002757 abstract void handleExecute() throws RemoteException, IOException;
Kenny Roota02b8b02010-08-05 16:14:17 -07002758 abstract void handleError();
Kenny Root38cf8862010-09-26 14:18:51 -07002759
2760 protected ObbInfo getObbInfo() throws IOException {
2761 ObbInfo obbInfo;
2762 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002763 obbInfo = mContainerService.getObbInfo(mObbState.ownerPath);
Kenny Root38cf8862010-09-26 14:18:51 -07002764 } catch (RemoteException e) {
2765 Slog.d(TAG, "Couldn't call DefaultContainerService to fetch OBB info for "
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002766 + mObbState.ownerPath);
Kenny Root38cf8862010-09-26 14:18:51 -07002767 obbInfo = null;
2768 }
2769 if (obbInfo == null) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002770 throw new IOException("Couldn't read OBB file: " + mObbState.ownerPath);
Kenny Root38cf8862010-09-26 14:18:51 -07002771 }
2772 return obbInfo;
2773 }
2774
Kenny Rootaf9d6672010-10-08 09:21:39 -07002775 protected void sendNewStatusOrIgnore(int status) {
2776 if (mObbState == null || mObbState.token == null) {
2777 return;
2778 }
2779
Kenny Root38cf8862010-09-26 14:18:51 -07002780 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002781 mObbState.token.onObbResult(mObbState.rawPath, mObbState.nonce, status);
Kenny Root38cf8862010-09-26 14:18:51 -07002782 } catch (RemoteException e) {
2783 Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
2784 }
2785 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002786 }
2787
2788 class MountObbAction extends ObbAction {
Ben Komalo444eca22011-09-01 15:17:44 -07002789 private final String mKey;
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002790 private final int mCallingUid;
Kenny Roota02b8b02010-08-05 16:14:17 -07002791
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002792 MountObbAction(ObbState obbState, String key, int callingUid) {
Kenny Roota02b8b02010-08-05 16:14:17 -07002793 super(obbState);
2794 mKey = key;
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002795 mCallingUid = callingUid;
Kenny Roota02b8b02010-08-05 16:14:17 -07002796 }
2797
Jason parks5af0b912010-11-29 09:05:25 -06002798 @Override
Kenny Root735de3b2010-09-30 14:11:39 -07002799 public void handleExecute() throws IOException, RemoteException {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002800 waitForReady();
2801 warnOnNotMounted();
2802
Kenny Root38cf8862010-09-26 14:18:51 -07002803 final ObbInfo obbInfo = getObbInfo();
2804
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002805 if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mCallingUid)) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002806 Slog.w(TAG, "Denied attempt to mount OBB " + obbInfo.filename
2807 + " which is owned by " + obbInfo.packageName);
2808 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
2809 return;
Kenny Roota02b8b02010-08-05 16:14:17 -07002810 }
2811
Kenny Rootaf9d6672010-10-08 09:21:39 -07002812 final boolean isMounted;
2813 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002814 isMounted = mObbPathToStateMap.containsKey(mObbState.rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002815 }
2816 if (isMounted) {
2817 Slog.w(TAG, "Attempt to mount OBB which is already mounted: " + obbInfo.filename);
2818 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_ALREADY_MOUNTED);
2819 return;
2820 }
2821
Kenny Rootaf9d6672010-10-08 09:21:39 -07002822 final String hashedKey;
2823 if (mKey == null) {
2824 hashedKey = "none";
2825 } else {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002826 try {
Kenny Root3b1abba2010-10-13 15:00:07 -07002827 SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
2828
2829 KeySpec ks = new PBEKeySpec(mKey.toCharArray(), obbInfo.salt,
2830 PBKDF2_HASH_ROUNDS, CRYPTO_ALGORITHM_KEY_SIZE);
2831 SecretKey key = factory.generateSecret(ks);
2832 BigInteger bi = new BigInteger(key.getEncoded());
2833 hashedKey = bi.toString(16);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002834 } catch (NoSuchAlgorithmException e) {
Kenny Root3b1abba2010-10-13 15:00:07 -07002835 Slog.e(TAG, "Could not load PBKDF2 algorithm", e);
2836 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
2837 return;
2838 } catch (InvalidKeySpecException e) {
2839 Slog.e(TAG, "Invalid key spec when loading PBKDF2 algorithm", e);
2840 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
Kenny Root38cf8862010-09-26 14:18:51 -07002841 return;
2842 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07002843 }
Kenny Root38cf8862010-09-26 14:18:51 -07002844
Kenny Rootaf9d6672010-10-08 09:21:39 -07002845 int rc = StorageResultCode.OperationSucceeded;
Kenny Rootaf9d6672010-10-08 09:21:39 -07002846 try {
Jeff Sharkey56cd6462013-06-07 15:09:15 -07002847 mConnector.execute("obb", "mount", mObbState.voldPath, new SensitiveArg(hashedKey),
2848 mObbState.ownerGid);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002849 } catch (NativeDaemonConnectorException e) {
2850 int code = e.getCode();
2851 if (code != VoldResponseCode.OpFailedStorageBusy) {
2852 rc = StorageResultCode.OperationFailedInternalError;
Kenny Roota02b8b02010-08-05 16:14:17 -07002853 }
2854 }
2855
Kenny Rootaf9d6672010-10-08 09:21:39 -07002856 if (rc == StorageResultCode.OperationSucceeded) {
2857 if (DEBUG_OBB)
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002858 Slog.d(TAG, "Successfully mounted OBB " + mObbState.voldPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002859
2860 synchronized (mObbMounts) {
2861 addObbStateLocked(mObbState);
2862 }
2863
2864 sendNewStatusOrIgnore(OnObbStateChangeListener.MOUNTED);
Kenny Root02c87302010-07-01 08:10:18 -07002865 } else {
Kenny Root05105f72010-09-22 17:29:43 -07002866 Slog.e(TAG, "Couldn't mount OBB file: " + rc);
Kenny Roota02b8b02010-08-05 16:14:17 -07002867
Kenny Rootaf9d6672010-10-08 09:21:39 -07002868 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT);
Kenny Root02c87302010-07-01 08:10:18 -07002869 }
2870 }
2871
Jason parks5af0b912010-11-29 09:05:25 -06002872 @Override
Kenny Roota02b8b02010-08-05 16:14:17 -07002873 public void handleError() {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002874 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
Kenny Root02c87302010-07-01 08:10:18 -07002875 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002876
2877 @Override
2878 public String toString() {
2879 StringBuilder sb = new StringBuilder();
2880 sb.append("MountObbAction{");
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002881 sb.append(mObbState);
Kenny Roota02b8b02010-08-05 16:14:17 -07002882 sb.append('}');
2883 return sb.toString();
2884 }
2885 }
2886
2887 class UnmountObbAction extends ObbAction {
Ben Komalo444eca22011-09-01 15:17:44 -07002888 private final boolean mForceUnmount;
Kenny Roota02b8b02010-08-05 16:14:17 -07002889
2890 UnmountObbAction(ObbState obbState, boolean force) {
2891 super(obbState);
2892 mForceUnmount = force;
2893 }
2894
Jason parks5af0b912010-11-29 09:05:25 -06002895 @Override
Kenny Root38cf8862010-09-26 14:18:51 -07002896 public void handleExecute() throws IOException {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002897 waitForReady();
2898 warnOnNotMounted();
2899
Kenny Root38cf8862010-09-26 14:18:51 -07002900 final ObbInfo obbInfo = getObbInfo();
Kenny Roota02b8b02010-08-05 16:14:17 -07002901
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002902 final ObbState existingState;
Kenny Root38cf8862010-09-26 14:18:51 -07002903 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002904 existingState = mObbPathToStateMap.get(mObbState.rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002905 }
Kenny Root38cf8862010-09-26 14:18:51 -07002906
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002907 if (existingState == null) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002908 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_NOT_MOUNTED);
2909 return;
2910 }
2911
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002912 if (existingState.ownerGid != mObbState.ownerGid) {
2913 Slog.w(TAG, "Permission denied attempting to unmount OBB " + existingState.rawPath
2914 + " (owned by GID " + existingState.ownerGid + ")");
Kenny Rootaf9d6672010-10-08 09:21:39 -07002915 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
2916 return;
2917 }
2918
Kenny Rootaf9d6672010-10-08 09:21:39 -07002919 int rc = StorageResultCode.OperationSucceeded;
Kenny Rootaf9d6672010-10-08 09:21:39 -07002920 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002921 final Command cmd = new Command("obb", "unmount", mObbState.voldPath);
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002922 if (mForceUnmount) {
2923 cmd.appendArg("force");
2924 }
2925 mConnector.execute(cmd);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002926 } catch (NativeDaemonConnectorException e) {
2927 int code = e.getCode();
2928 if (code == VoldResponseCode.OpFailedStorageBusy) {
2929 rc = StorageResultCode.OperationFailedStorageBusy;
2930 } else if (code == VoldResponseCode.OpFailedStorageNotFound) {
2931 // If it's not mounted then we've already won.
2932 rc = StorageResultCode.OperationSucceeded;
2933 } else {
2934 rc = StorageResultCode.OperationFailedInternalError;
Kenny Roota02b8b02010-08-05 16:14:17 -07002935 }
2936 }
2937
Kenny Rootaf9d6672010-10-08 09:21:39 -07002938 if (rc == StorageResultCode.OperationSucceeded) {
2939 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002940 removeObbStateLocked(existingState);
Kenny Root38cf8862010-09-26 14:18:51 -07002941 }
2942
Kenny Rootaf9d6672010-10-08 09:21:39 -07002943 sendNewStatusOrIgnore(OnObbStateChangeListener.UNMOUNTED);
Kenny Roota02b8b02010-08-05 16:14:17 -07002944 } else {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002945 Slog.w(TAG, "Could not unmount OBB: " + existingState);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002946 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_UNMOUNT);
Kenny Roota02b8b02010-08-05 16:14:17 -07002947 }
2948 }
2949
Jason parks5af0b912010-11-29 09:05:25 -06002950 @Override
Kenny Roota02b8b02010-08-05 16:14:17 -07002951 public void handleError() {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002952 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
Kenny Roota02b8b02010-08-05 16:14:17 -07002953 }
2954
2955 @Override
2956 public String toString() {
2957 StringBuilder sb = new StringBuilder();
2958 sb.append("UnmountObbAction{");
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002959 sb.append(mObbState);
Kenny Roota02b8b02010-08-05 16:14:17 -07002960 sb.append(",force=");
2961 sb.append(mForceUnmount);
Kenny Roota02b8b02010-08-05 16:14:17 -07002962 sb.append('}');
2963 return sb.toString();
2964 }
Kenny Root02c87302010-07-01 08:10:18 -07002965 }
Kenny Root38cf8862010-09-26 14:18:51 -07002966
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -08002967 @VisibleForTesting
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002968 public static String buildObbPath(final String canonicalPath, int userId, boolean forVold) {
2969 // TODO: allow caller to provide Environment for full testing
Jeff Sharkey1abdb712013-08-11 16:28:14 -07002970 // TODO: extend to support OBB mounts on secondary external storage
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002971
2972 // Only adjust paths when storage is emulated
2973 if (!Environment.isExternalStorageEmulated()) {
2974 return canonicalPath;
2975 }
2976
2977 String path = canonicalPath.toString();
2978
2979 // First trim off any external storage prefix
2980 final UserEnvironment userEnv = new UserEnvironment(userId);
2981
2982 // /storage/emulated/0
Jeff Sharkey1abdb712013-08-11 16:28:14 -07002983 final String externalPath = userEnv.getExternalStorageDirectory().getAbsolutePath();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002984 // /storage/emulated_legacy
2985 final String legacyExternalPath = Environment.getLegacyExternalStorageDirectory()
Jeff Sharkey1abdb712013-08-11 16:28:14 -07002986 .getAbsolutePath();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002987
2988 if (path.startsWith(externalPath)) {
2989 path = path.substring(externalPath.length() + 1);
2990 } else if (path.startsWith(legacyExternalPath)) {
2991 path = path.substring(legacyExternalPath.length() + 1);
2992 } else {
2993 return canonicalPath;
2994 }
2995
2996 // Handle special OBB paths on emulated storage
2997 final String obbPath = "Android/obb";
2998 if (path.startsWith(obbPath)) {
2999 path = path.substring(obbPath.length() + 1);
3000
3001 if (forVold) {
Jeff Sharkey1abdb712013-08-11 16:28:14 -07003002 return new File(Environment.getEmulatedStorageObbSource(), path).getAbsolutePath();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003003 } else {
3004 final UserEnvironment ownerEnv = new UserEnvironment(UserHandle.USER_OWNER);
Jeff Sharkey1abdb712013-08-11 16:28:14 -07003005 return new File(ownerEnv.buildExternalStorageAndroidObbDirs()[0], path)
3006 .getAbsolutePath();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003007 }
3008 }
3009
3010 // Handle normal external storage paths
3011 if (forVold) {
Jeff Sharkey1abdb712013-08-11 16:28:14 -07003012 return new File(Environment.getEmulatedStorageSource(userId), path).getAbsolutePath();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003013 } else {
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07003014 return new File(userEnv.getExternalDirsForApp()[0], path).getAbsolutePath();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003015 }
3016 }
3017
Kenny Root38cf8862010-09-26 14:18:51 -07003018 @Override
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003019 protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
3020 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
3021
3022 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ", 160);
Kenny Root38cf8862010-09-26 14:18:51 -07003023
Kenny Root38cf8862010-09-26 14:18:51 -07003024 synchronized (mObbMounts) {
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003025 pw.println("mObbMounts:");
3026 pw.increaseIndent();
3027 final Iterator<Entry<IBinder, List<ObbState>>> binders = mObbMounts.entrySet()
3028 .iterator();
Kenny Rootaf9d6672010-10-08 09:21:39 -07003029 while (binders.hasNext()) {
3030 Entry<IBinder, List<ObbState>> e = binders.next();
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003031 pw.println(e.getKey() + ":");
3032 pw.increaseIndent();
Kenny Rootaf9d6672010-10-08 09:21:39 -07003033 final List<ObbState> obbStates = e.getValue();
Kenny Root38cf8862010-09-26 14:18:51 -07003034 for (final ObbState obbState : obbStates) {
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003035 pw.println(obbState);
Kenny Root38cf8862010-09-26 14:18:51 -07003036 }
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003037 pw.decreaseIndent();
Kenny Root38cf8862010-09-26 14:18:51 -07003038 }
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003039 pw.decreaseIndent();
Kenny Rootaf9d6672010-10-08 09:21:39 -07003040
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003041 pw.println();
3042 pw.println("mObbPathToStateMap:");
3043 pw.increaseIndent();
Kenny Rootaf9d6672010-10-08 09:21:39 -07003044 final Iterator<Entry<String, ObbState>> maps = mObbPathToStateMap.entrySet().iterator();
3045 while (maps.hasNext()) {
3046 final Entry<String, ObbState> e = maps.next();
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003047 pw.print(e.getKey());
3048 pw.print(" -> ");
3049 pw.println(e.getValue());
Kenny Rootaf9d6672010-10-08 09:21:39 -07003050 }
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003051 pw.decreaseIndent();
Kenny Root38cf8862010-09-26 14:18:51 -07003052 }
Kenny Root4161f9b2011-07-13 09:48:33 -07003053
Jeff Sharkeyb049e212012-09-07 23:16:01 -07003054 synchronized (mVolumesLock) {
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003055 pw.println();
3056 pw.println("mVolumes:");
3057 pw.increaseIndent();
3058 for (StorageVolume volume : mVolumes) {
3059 pw.println(volume);
3060 pw.increaseIndent();
3061 pw.println("Current state: " + mVolumeStates.get(volume.getPath()));
3062 pw.decreaseIndent();
Kenny Root4161f9b2011-07-13 09:48:33 -07003063 }
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003064 pw.decreaseIndent();
Kenny Root4161f9b2011-07-13 09:48:33 -07003065 }
Robert Greenwalt470fd722012-01-18 12:51:15 -08003066
3067 pw.println();
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003068 pw.println("mConnection:");
3069 pw.increaseIndent();
Robert Greenwalt470fd722012-01-18 12:51:15 -08003070 mConnector.dump(fd, pw, args);
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003071 pw.decreaseIndent();
Kenny Root38cf8862010-09-26 14:18:51 -07003072 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003073
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -07003074 /** {@inheritDoc} */
3075 public void monitor() {
3076 if (mConnector != null) {
3077 mConnector.monitor();
3078 }
3079 }
3080}