blob: 4c937f74067b805a5a40d8e663b3d1d9214b6528 [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 Sharkeyd95d3bf2015-04-14 21:39:44 -070019import static com.android.internal.util.XmlUtils.readIntAttribute;
20import static com.android.internal.util.XmlUtils.readStringAttribute;
21import static com.android.internal.util.XmlUtils.writeIntAttribute;
22import static com.android.internal.util.XmlUtils.writeStringAttribute;
23import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
24import static org.xmlpull.v1.XmlPullParser.START_TAG;
25
Jason parks8888c592011-01-20 22:46:41 -060026import android.Manifest;
Elliott Hughesf839b4f2014-09-26 12:30:47 -070027import android.app.ActivityManagerNative;
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -070028import android.app.AppOpsManager;
Kenny Roota02b8b02010-08-05 16:14:17 -070029import android.content.ComponentName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.content.Context;
31import android.content.Intent;
Kenny Roota02b8b02010-08-05 16:14:17 -070032import android.content.ServiceConnection;
Elliott Hughesf839b4f2014-09-26 12:30:47 -070033import android.content.res.Configuration;
Kenny Root02c87302010-07-01 08:10:18 -070034import android.content.res.ObbInfo;
Jeff Sharkey48877892015-03-18 11:27:19 -070035import android.mtp.MtpStorage;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.net.Uri;
Kenny Root02c87302010-07-01 08:10:18 -070037import android.os.Binder;
Kenny Roota02b8b02010-08-05 16:14:17 -070038import android.os.Environment;
Jeff Sharkeyb049e212012-09-07 23:16:01 -070039import android.os.Environment.UserEnvironment;
Jeff Sharkey48877892015-03-18 11:27:19 -070040import android.os.FileUtils;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080041import android.os.Handler;
Dianne Hackbornefa92b22013-05-03 14:11:43 -070042import android.os.HandlerThread;
Kenny Roota02b8b02010-08-05 16:14:17 -070043import android.os.IBinder;
Daniel Sandler5f27ef42010-03-16 15:42:02 -040044import android.os.Looper;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080045import android.os.Message;
Jeff Sharkey7151a9a2015-04-04 15:22:37 -070046import android.os.RemoteCallbackList;
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;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070053import android.os.storage.DiskInfo;
Kenny Roota02b8b02010-08-05 16:14:17 -070054import android.os.storage.IMountService;
55import android.os.storage.IMountServiceListener;
56import android.os.storage.IMountShutdownObserver;
57import android.os.storage.IObbActionListener;
Kenny Rootaf9d6672010-10-08 09:21:39 -070058import android.os.storage.OnObbStateChangeListener;
Paul Lawrence46791e72014-04-03 09:10:26 -070059import android.os.storage.StorageManager;
Kenny Roota02b8b02010-08-05 16:14:17 -070060import android.os.storage.StorageResultCode;
Mike Lockwood2f6a3882011-05-09 19:08:06 -070061import android.os.storage.StorageVolume;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070062import android.os.storage.VolumeInfo;
Jason parksf7b3cd42011-01-27 09:28:25 -060063import android.text.TextUtils;
Jeff Sharkey48877892015-03-18 11:27:19 -070064import android.util.ArrayMap;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070065import android.util.AtomicFile;
66import android.util.DebugUtils;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070067import android.util.Log;
San Mehata5078592010-03-25 09:36:54 -070068import android.util.Slog;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070069import android.util.Xml;
Jeff Sharkey48877892015-03-18 11:27:19 -070070
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070071import libcore.io.IoUtils;
Jeff Sharkey48877892015-03-18 11:27:19 -070072import libcore.util.EmptyArray;
73import libcore.util.HexEncoding;
Mike Lockwood2f6a3882011-05-09 19:08:06 -070074
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -080075import com.android.internal.annotations.GuardedBy;
76import com.android.internal.annotations.VisibleForTesting;
Jeff Sharkeyb049e212012-09-07 23:16:01 -070077import com.android.internal.app.IMediaContainerService;
Jeff Sharkey7151a9a2015-04-04 15:22:37 -070078import com.android.internal.os.SomeArgs;
Jeff Sharkey48877892015-03-18 11:27:19 -070079import com.android.internal.util.ArrayUtils;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070080import com.android.internal.util.FastXmlSerializer;
Jeff Sharkey5aca2b82013-10-16 16:21:54 -070081import com.android.internal.util.IndentingPrintWriter;
Jeff Sharkey4fbbda42012-09-24 18:34:07 -070082import com.android.internal.util.Preconditions;
Jeff Sharkeyb049e212012-09-07 23:16:01 -070083import com.android.server.NativeDaemonConnector.Command;
Jeff Sharkey56cd6462013-06-07 15:09:15 -070084import com.android.server.NativeDaemonConnector.SensitiveArg;
Jeff Sharkeyb049e212012-09-07 23:16:01 -070085import com.android.server.pm.PackageManagerService;
Kenny Roota02b8b02010-08-05 16:14:17 -070086
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070087import org.xmlpull.v1.XmlPullParser;
88import org.xmlpull.v1.XmlPullParserException;
89import org.xmlpull.v1.XmlSerializer;
90
Jeff Sharkeyb049e212012-09-07 23:16:01 -070091import java.io.File;
Kenny Root38cf8862010-09-26 14:18:51 -070092import java.io.FileDescriptor;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070093import java.io.FileInputStream;
94import java.io.FileNotFoundException;
Christopher Tate7265abe2014-11-21 13:54:45 -080095import java.io.FileOutputStream;
Kenny Root05105f72010-09-22 17:29:43 -070096import java.io.IOException;
Kenny Root38cf8862010-09-26 14:18:51 -070097import java.io.PrintWriter;
Kenny Root3b1abba2010-10-13 15:00:07 -070098import java.math.BigInteger;
Paul Lawrence8e397362014-01-27 15:22:30 -080099import java.nio.charset.StandardCharsets;
Kenny Root735de3b2010-09-30 14:11:39 -0700100import java.security.NoSuchAlgorithmException;
Kenny Root3b1abba2010-10-13 15:00:07 -0700101import java.security.spec.InvalidKeySpecException;
102import java.security.spec.KeySpec;
Christopher Tate7265abe2014-11-21 13:54:45 -0800103import java.text.SimpleDateFormat;
San Mehat22dd86e2010-01-12 12:21:18 -0800104import java.util.ArrayList;
Christopher Tate7265abe2014-11-21 13:54:45 -0800105import java.util.Date;
Kenny Roota02b8b02010-08-05 16:14:17 -0700106import java.util.HashMap;
San Mehat6cdd9c02010-02-09 14:45:20 -0800107import java.util.HashSet;
Kenny Root38cf8862010-09-26 14:18:51 -0700108import java.util.Iterator;
Kenny Roota02b8b02010-08-05 16:14:17 -0700109import java.util.LinkedList;
110import java.util.List;
Elliott Hughesf839b4f2014-09-26 12:30:47 -0700111import java.util.Locale;
Kenny Roota02b8b02010-08-05 16:14:17 -0700112import java.util.Map;
Kenny Root38cf8862010-09-26 14:18:51 -0700113import java.util.Map.Entry;
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700114import java.util.Objects;
Kenny Root51a573c2012-05-17 13:30:28 -0700115import java.util.concurrent.CountDownLatch;
116import java.util.concurrent.TimeUnit;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800117
Kenny Root3b1abba2010-10-13 15:00:07 -0700118import javax.crypto.SecretKey;
119import javax.crypto.SecretKeyFactory;
120import javax.crypto.spec.PBEKeySpec;
121
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800122/**
Jeff Sharkey48877892015-03-18 11:27:19 -0700123 * Service responsible for various storage media. Connects to {@code vold} to
124 * watch for and manage dynamically added storage, such as SD cards and USB mass
125 * storage. Also decides how storage should be presented to users on the device.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800126 */
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -0700127class MountService extends IMountService.Stub
128 implements INativeDaemonConnectorCallbacks, Watchdog.Monitor {
Jason parks5af0b912010-11-29 09:05:25 -0600129
Jeff Sharkey48877892015-03-18 11:27:19 -0700130 // TODO: finish enforcing UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA
131
Christopher Tated417d622013-08-19 16:14:25 -0700132 // Static direct instance pointer for the tightly-coupled idle service to use
133 static MountService sSelf = null;
134
Jeff Sharkey56e62932015-03-21 20:41:00 -0700135 public static class Lifecycle extends SystemService {
136 private MountService mMountService;
137
138 public Lifecycle(Context context) {
139 super(context);
140 }
141
142 @Override
143 public void onStart() {
144 mMountService = new MountService(getContext());
145 publishBinderService("mount", mMountService);
146 }
147
148 @Override
149 public void onBootPhase(int phase) {
150 if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
151 mMountService.systemReady();
152 }
153 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700154
155 @Override
156 public void onStartUser(int userHandle) {
157 mMountService.onStartUser(userHandle);
158 }
159
160 @Override
161 public void onCleanupUser(int userHandle) {
162 mMountService.onCleanupUser(userHandle);
163 }
Jeff Sharkey56e62932015-03-21 20:41:00 -0700164 }
165
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800166 private static final boolean LOCAL_LOGD = false;
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800167 private static final boolean DEBUG_EVENTS = false;
Kenny Rootb7db2722011-01-25 16:39:35 -0800168 private static final boolean DEBUG_OBB = false;
Kenny Root02c87302010-07-01 08:10:18 -0700169
Kenny Root07714d42011-08-17 17:49:28 -0700170 // Disable this since it messes up long-running cryptfs operations.
171 private static final boolean WATCHDOG_ENABLE = false;
172
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800173 private static final String TAG = "MountService";
174
Kenny Root305bcbf2010-09-03 07:56:38 -0700175 private static final String VOLD_TAG = "VoldConnector";
176
Kenny Rootcf0b38c2011-03-22 14:17:59 -0700177 /** Maximum number of ASEC containers allowed to be mounted. */
178 private static final int MAX_CONTAINERS = 250;
179
San Mehat4270e1e2010-01-29 05:32:19 -0800180 /*
181 * Internal vold response code constants
182 */
San Mehat22dd86e2010-01-12 12:21:18 -0800183 class VoldResponseCode {
San Mehat4270e1e2010-01-29 05:32:19 -0800184 /*
185 * 100 series - Requestion action was initiated; expect another reply
186 * before proceeding with a new command.
187 */
San Mehat22dd86e2010-01-12 12:21:18 -0800188 public static final int VolumeListResult = 110;
189 public static final int AsecListResult = 111;
San Mehatc1b4ce92010-02-16 17:13:03 -0800190 public static final int StorageUsersListResult = 112;
Paul Lawrencee51dcf92014-03-18 10:56:00 -0700191 public static final int CryptfsGetfieldResult = 113;
San Mehat22dd86e2010-01-12 12:21:18 -0800192
San Mehat4270e1e2010-01-29 05:32:19 -0800193 /*
194 * 200 series - Requestion action has been successfully completed.
195 */
196 public static final int ShareStatusResult = 210;
San Mehat22dd86e2010-01-12 12:21:18 -0800197 public static final int AsecPathResult = 211;
San Mehat4270e1e2010-01-29 05:32:19 -0800198 public static final int ShareEnabledResult = 212;
San Mehat22dd86e2010-01-12 12:21:18 -0800199
San Mehat4270e1e2010-01-29 05:32:19 -0800200 /*
201 * 400 series - Command was accepted, but the requested action
202 * did not take place.
203 */
204 public static final int OpFailedNoMedia = 401;
205 public static final int OpFailedMediaBlank = 402;
206 public static final int OpFailedMediaCorrupt = 403;
207 public static final int OpFailedVolNotMounted = 404;
San Mehatd9709982010-02-18 11:43:03 -0800208 public static final int OpFailedStorageBusy = 405;
San Mehat2d66cef2010-03-23 11:12:52 -0700209 public static final int OpFailedStorageNotFound = 406;
San Mehat4270e1e2010-01-29 05:32:19 -0800210
211 /*
212 * 600 series - Unsolicited broadcasts.
213 */
Jeff Sharkey48877892015-03-18 11:27:19 -0700214 public static final int DISK_CREATED = 640;
215 public static final int DISK_SIZE_CHANGED = 641;
216 public static final int DISK_LABEL_CHANGED = 642;
217 public static final int DISK_VOLUME_CREATED = 643;
Jeff Sharkey59d577a2015-04-11 21:27:21 -0700218 public static final int DISK_VOLUME_DESTROYED = 644;
Jeff Sharkey48877892015-03-18 11:27:19 -0700219 public static final int DISK_DESTROYED = 649;
220
221 public static final int VOLUME_CREATED = 650;
222 public static final int VOLUME_STATE_CHANGED = 651;
223 public static final int VOLUME_FS_TYPE_CHANGED = 652;
224 public static final int VOLUME_FS_UUID_CHANGED = 653;
225 public static final int VOLUME_FS_LABEL_CHANGED = 654;
226 public static final int VOLUME_PATH_CHANGED = 655;
227 public static final int VOLUME_DESTROYED = 659;
Svetoslavf23b64d2013-04-25 14:45:54 -0700228
229 /*
230 * 700 series - fstrim
231 */
232 public static final int FstrimCompleted = 700;
San Mehat22dd86e2010-01-12 12:21:18 -0800233 }
234
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700235 private static final String TAG_VOLUMES = "volumes";
236 private static final String TAG_VOLUME = "volume";
237 private static final String ATTR_TYPE = "type";
238 private static final String ATTR_FS_UUID = "fsUuid";
239 private static final String ATTR_NICKNAME = "nickname";
240 private static final String ATTR_USER_FLAGS = "userFlags";
241
242 private final AtomicFile mMetadataFile;
243
244 private static class VolumeMetadata {
245 public final int type;
246 public final String fsUuid;
247 public String nickname;
248 public int userFlags;
249
250 public VolumeMetadata(int type, String fsUuid) {
251 this.type = type;
252 this.fsUuid = Preconditions.checkNotNull(fsUuid);
253 }
254
255 public static VolumeMetadata read(XmlPullParser in) throws IOException {
256 final int type = readIntAttribute(in, ATTR_TYPE);
257 final String fsUuid = readStringAttribute(in, ATTR_FS_UUID);
258 final VolumeMetadata meta = new VolumeMetadata(type, fsUuid);
259 meta.nickname = readStringAttribute(in, ATTR_NICKNAME);
260 meta.userFlags = readIntAttribute(in, ATTR_USER_FLAGS);
261 return meta;
262 }
263
264 public static void write(XmlSerializer out, VolumeMetadata meta) throws IOException {
265 out.startTag(null, TAG_VOLUME);
266 writeIntAttribute(out, ATTR_TYPE, meta.type);
267 writeStringAttribute(out, ATTR_FS_UUID, meta.fsUuid);
268 writeStringAttribute(out, ATTR_NICKNAME, meta.nickname);
269 writeIntAttribute(out, ATTR_USER_FLAGS, meta.userFlags);
270 out.endTag(null, TAG_VOLUME);
271 }
272
273 public void dump(IndentingPrintWriter pw) {
274 pw.println("VolumeMetadata:");
275 pw.increaseIndent();
276 pw.printPair("type", DebugUtils.valueToString(VolumeInfo.class, "TYPE_", type));
277 pw.printPair("fsUuid", fsUuid);
278 pw.printPair("nickname", nickname);
279 pw.printPair("userFlags",
280 DebugUtils.flagsToString(VolumeInfo.class, "USER_FLAG_", userFlags));
281 pw.decreaseIndent();
282 pw.println();
283 }
284 }
285
Jeff Sharkey48877892015-03-18 11:27:19 -0700286 /**
287 * <em>Never</em> hold the lock while performing downcalls into vold, since
288 * unsolicited events can suddenly appear to update data structures.
289 */
290 private final Object mLock = new Object();
291
292 @GuardedBy("mLock")
293 private int[] mStartedUsers = EmptyArray.INT;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700294
295 /** Map from disk ID to disk */
Jeff Sharkey48877892015-03-18 11:27:19 -0700296 @GuardedBy("mLock")
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700297 private ArrayMap<String, DiskInfo> mDisks = new ArrayMap<>();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700298 /** Map from volume ID to disk */
Jeff Sharkey48877892015-03-18 11:27:19 -0700299 @GuardedBy("mLock")
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700300 private ArrayMap<String, VolumeInfo> mVolumes = new ArrayMap<>();
Jeff Sharkey48877892015-03-18 11:27:19 -0700301
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700302 /** Map from UUID to metadata */
303 @GuardedBy("mLock")
304 private ArrayMap<String, VolumeMetadata> mMetadata = new ArrayMap<>();
305
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700306 private DiskInfo findDiskById(String id) {
307 synchronized (mLock) {
308 final DiskInfo disk = mDisks.get(id);
309 if (disk != null) {
310 return disk;
311 }
312 }
313 throw new IllegalArgumentException("No disk found for ID " + id);
314 }
315
316 private VolumeInfo findVolumeById(String id) {
317 synchronized (mLock) {
318 final VolumeInfo vol = mVolumes.get(id);
319 if (vol != null) {
320 return vol;
321 }
322 }
323 throw new IllegalArgumentException("No volume found for ID " + id);
324 }
325
Jeff Sharkey48877892015-03-18 11:27:19 -0700326 @Deprecated
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700327 private String findVolumeIdForPath(String path) {
Jeff Sharkey48877892015-03-18 11:27:19 -0700328 synchronized (mLock) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700329 for (int i = 0; i < mVolumes.size(); i++) {
330 final VolumeInfo vol = mVolumes.valueAt(i);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700331 if (vol.path != null && path.startsWith(vol.path)) {
332 return vol.id;
Jeff Sharkey48877892015-03-18 11:27:19 -0700333 }
334 }
335 }
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700336 throw new IllegalArgumentException("No volume found for path " + path);
Jeff Sharkey48877892015-03-18 11:27:19 -0700337 }
338
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700339 private VolumeMetadata findOrCreateMetadataLocked(VolumeInfo vol) {
340 VolumeMetadata meta = mMetadata.get(vol.fsUuid);
341 if (meta == null) {
342 meta = new VolumeMetadata(vol.type, vol.fsUuid);
343 mMetadata.put(meta.fsUuid, meta);
344 }
345 return meta;
346 }
347
Jeff Sharkey48877892015-03-18 11:27:19 -0700348 private static int sNextMtpIndex = 1;
349
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700350 private static int allocateMtpIndex(String volId) {
351 if (VolumeInfo.ID_EMULATED_INTERNAL.equals(volId)) {
352 return 0;
353 } else {
354 return sNextMtpIndex++;
Jeff Sharkey48877892015-03-18 11:27:19 -0700355 }
356 }
357
Paul Lawrence8e397362014-01-27 15:22:30 -0800358 /** List of crypto types.
359 * These must match CRYPT_TYPE_XXX in cryptfs.h AND their
360 * corresponding commands in CommandListener.cpp */
361 public static final String[] CRYPTO_TYPES
362 = { "password", "default", "pattern", "pin" };
363
Brian Carlstrom7395a8a2014-04-28 22:11:01 -0700364 private final Context mContext;
Brian Carlstromdfad99a2014-05-07 15:21:14 -0700365 private final NativeDaemonConnector mConnector;
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700366
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700367 private volatile boolean mSystemReady = false;
Jeff Sharkey48877892015-03-18 11:27:19 -0700368 private volatile boolean mDaemonConnected = false;
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700369
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700370 private PackageManagerService mPms;
371
372 private final Callbacks mCallbacks;
Jeff Sharkey48877892015-03-18 11:27:19 -0700373
Jeff Sharkey0be607c2012-11-14 14:39:19 -0800374 private final CountDownLatch mConnectedSignal = new CountDownLatch(1);
375 private final CountDownLatch mAsecsScanned = new CountDownLatch(1);
Jeff Sharkey48877892015-03-18 11:27:19 -0700376
377 private final Object mUnmountLock = new Object();
378 @GuardedBy("mUnmountLock")
379 private CountDownLatch mUnmountSignal;
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -0800380
San Mehat6cdd9c02010-02-09 14:45:20 -0800381 /**
382 * Private hash of currently mounted secure containers.
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800383 * Used as a lock in methods to manipulate secure containers.
San Mehat6cdd9c02010-02-09 14:45:20 -0800384 */
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800385 final private HashSet<String> mAsecMountSet = new HashSet<String>();
San Mehat6cdd9c02010-02-09 14:45:20 -0800386
Kenny Root02c87302010-07-01 08:10:18 -0700387 /**
Kenny Root3b1abba2010-10-13 15:00:07 -0700388 * The size of the crypto algorithm key in bits for OBB files. Currently
389 * Twofish is used which takes 128-bit keys.
390 */
391 private static final int CRYPTO_ALGORITHM_KEY_SIZE = 128;
392
393 /**
394 * The number of times to run SHA1 in the PBKDF2 function for OBB files.
395 * 1024 is reasonably secure and not too slow.
396 */
397 private static final int PBKDF2_HASH_ROUNDS = 1024;
398
399 /**
Kenny Roota02b8b02010-08-05 16:14:17 -0700400 * Mounted OBB tracking information. Used to track the current state of all
401 * OBBs.
Kenny Root02c87302010-07-01 08:10:18 -0700402 */
Kenny Root735de3b2010-09-30 14:11:39 -0700403 final private Map<IBinder, List<ObbState>> mObbMounts = new HashMap<IBinder, List<ObbState>>();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700404
405 /** Map from raw paths to {@link ObbState}. */
Kenny Roota02b8b02010-08-05 16:14:17 -0700406 final private Map<String, ObbState> mObbPathToStateMap = new HashMap<String, ObbState>();
407
408 class ObbState implements IBinder.DeathRecipient {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700409 public ObbState(String rawPath, String canonicalPath, int callingUid,
410 IObbActionListener token, int nonce) {
411 this.rawPath = rawPath;
412 this.canonicalPath = canonicalPath.toString();
413
414 final int userId = UserHandle.getUserId(callingUid);
415 this.ownerPath = buildObbPath(canonicalPath, userId, false);
416 this.voldPath = buildObbPath(canonicalPath, userId, true);
417
418 this.ownerGid = UserHandle.getSharedAppGid(callingUid);
Kenny Rootaf9d6672010-10-08 09:21:39 -0700419 this.token = token;
420 this.nonce = nonce;
Kenny Roota02b8b02010-08-05 16:14:17 -0700421 }
422
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700423 final String rawPath;
424 final String canonicalPath;
425 final String ownerPath;
426 final String voldPath;
Kenny Roota02b8b02010-08-05 16:14:17 -0700427
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700428 final int ownerGid;
Kenny Roota02b8b02010-08-05 16:14:17 -0700429
Kenny Rootaf9d6672010-10-08 09:21:39 -0700430 // Token of remote Binder caller
431 final IObbActionListener token;
432
433 // Identifier to pass back to the token
434 final int nonce;
Kenny Roota02b8b02010-08-05 16:14:17 -0700435
Kenny Root735de3b2010-09-30 14:11:39 -0700436 public IBinder getBinder() {
437 return token.asBinder();
438 }
439
Kenny Roota02b8b02010-08-05 16:14:17 -0700440 @Override
441 public void binderDied() {
442 ObbAction action = new UnmountObbAction(this, true);
443 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
Kenny Root735de3b2010-09-30 14:11:39 -0700444 }
Kenny Roota02b8b02010-08-05 16:14:17 -0700445
Kenny Root5919ac62010-10-05 09:49:40 -0700446 public void link() throws RemoteException {
447 getBinder().linkToDeath(this, 0);
448 }
449
450 public void unlink() {
Kenny Root735de3b2010-09-30 14:11:39 -0700451 getBinder().unlinkToDeath(this, 0);
Kenny Roota02b8b02010-08-05 16:14:17 -0700452 }
Kenny Root38cf8862010-09-26 14:18:51 -0700453
454 @Override
455 public String toString() {
456 StringBuilder sb = new StringBuilder("ObbState{");
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700457 sb.append("rawPath=").append(rawPath);
458 sb.append(",canonicalPath=").append(canonicalPath);
459 sb.append(",ownerPath=").append(ownerPath);
460 sb.append(",voldPath=").append(voldPath);
461 sb.append(",ownerGid=").append(ownerGid);
462 sb.append(",token=").append(token);
463 sb.append(",binder=").append(getBinder());
Kenny Root38cf8862010-09-26 14:18:51 -0700464 sb.append('}');
465 return sb.toString();
466 }
Kenny Roota02b8b02010-08-05 16:14:17 -0700467 }
468
469 // OBB Action Handler
470 final private ObbActionHandler mObbActionHandler;
471
472 // OBB action handler messages
473 private static final int OBB_RUN_ACTION = 1;
474 private static final int OBB_MCS_BOUND = 2;
475 private static final int OBB_MCS_UNBIND = 3;
476 private static final int OBB_MCS_RECONNECT = 4;
Kenny Rootaf9d6672010-10-08 09:21:39 -0700477 private static final int OBB_FLUSH_MOUNT_STATE = 5;
Kenny Roota02b8b02010-08-05 16:14:17 -0700478
479 /*
480 * Default Container Service information
481 */
482 static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
483 "com.android.defcontainer", "com.android.defcontainer.DefaultContainerService");
484
485 final private DefaultContainerConnection mDefContainerConn = new DefaultContainerConnection();
486
487 class DefaultContainerConnection implements ServiceConnection {
Jeff Sharkey48877892015-03-18 11:27:19 -0700488 @Override
Kenny Roota02b8b02010-08-05 16:14:17 -0700489 public void onServiceConnected(ComponentName name, IBinder service) {
490 if (DEBUG_OBB)
491 Slog.i(TAG, "onServiceConnected");
492 IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(service);
493 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_MCS_BOUND, imcs));
494 }
495
Jeff Sharkey48877892015-03-18 11:27:19 -0700496 @Override
Kenny Roota02b8b02010-08-05 16:14:17 -0700497 public void onServiceDisconnected(ComponentName name) {
498 if (DEBUG_OBB)
499 Slog.i(TAG, "onServiceDisconnected");
500 }
501 };
502
503 // Used in the ObbActionHandler
504 private IMediaContainerService mContainerService = null;
Kenny Root02c87302010-07-01 08:10:18 -0700505
Christopher Tate7265abe2014-11-21 13:54:45 -0800506 // Last fstrim operation tracking
507 private static final String LAST_FSTRIM_FILE = "last-fstrim";
508 private final File mLastMaintenanceFile;
509 private long mLastMaintenance;
510
Kenny Root02c87302010-07-01 08:10:18 -0700511 // Handler messages
Jeff Sharkey48877892015-03-18 11:27:19 -0700512 private static final int H_SYSTEM_READY = 1;
513 private static final int H_DAEMON_CONNECTED = 2;
514 private static final int H_SHUTDOWN = 3;
515 private static final int H_FSTRIM = 4;
516 private static final int H_VOLUME_MOUNT = 5;
517 private static final int H_VOLUME_BROADCAST = 6;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800518
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400519 class MountServiceHandler extends Handler {
Jeff Sharkey48877892015-03-18 11:27:19 -0700520 public MountServiceHandler(Looper looper) {
521 super(looper);
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400522 }
523
Jason parks5af0b912010-11-29 09:05:25 -0600524 @Override
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800525 public void handleMessage(Message msg) {
526 switch (msg.what) {
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700527 case H_SYSTEM_READY: {
Jeff Sharkey48877892015-03-18 11:27:19 -0700528 handleSystemReady();
529 break;
530 }
531 case H_DAEMON_CONNECTED: {
532 handleDaemonConnected();
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700533 break;
534 }
Christopher Tated417d622013-08-19 16:14:25 -0700535 case H_FSTRIM: {
536 waitForReady();
537 Slog.i(TAG, "Running fstrim idle maintenance");
Christopher Tate7265abe2014-11-21 13:54:45 -0800538
539 // Remember when we kicked it off
540 try {
541 mLastMaintenance = System.currentTimeMillis();
542 mLastMaintenanceFile.setLastModified(mLastMaintenance);
543 } catch (Exception e) {
544 Slog.e(TAG, "Unable to record last fstrim!");
545 }
546
Christopher Tated417d622013-08-19 16:14:25 -0700547 try {
548 // This method must be run on the main (handler) thread,
549 // so it is safe to directly call into vold.
550 mConnector.execute("fstrim", "dotrim");
551 EventLogTags.writeFstrimStart(SystemClock.elapsedRealtime());
552 } catch (NativeDaemonConnectorException ndce) {
553 Slog.e(TAG, "Failed to run fstrim!");
554 }
Christopher Tate7265abe2014-11-21 13:54:45 -0800555
Christopher Tated417d622013-08-19 16:14:25 -0700556 // invoke the completion callback, if any
557 Runnable callback = (Runnable) msg.obj;
558 if (callback != null) {
559 callback.run();
560 }
561 break;
562 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700563 case H_SHUTDOWN: {
564 final IMountShutdownObserver obs = (IMountShutdownObserver) msg.obj;
565 boolean success = false;
566 try {
567 success = mConnector.execute("volume", "shutdown").isClassOk();
568 } catch (NativeDaemonConnectorException ignored) {
569 }
570 if (obs != null) {
571 try {
572 obs.onShutDownComplete(success ? 0 : -1);
573 } catch (RemoteException ignored) {
574 }
575 }
576 break;
577 }
578 case H_VOLUME_MOUNT: {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700579 final VolumeInfo vol = (VolumeInfo) msg.obj;
Jeff Sharkey48877892015-03-18 11:27:19 -0700580 try {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700581 mConnector.execute("volume", "mount", vol.id, vol.flags, vol.userId);
Jeff Sharkey48877892015-03-18 11:27:19 -0700582 } catch (NativeDaemonConnectorException ignored) {
583 }
584 break;
585 }
586 case H_VOLUME_BROADCAST: {
587 final StorageVolume userVol = (StorageVolume) msg.obj;
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700588 final String envState = userVol.getState();
589 Slog.d(TAG, "Volume " + userVol.getId() + " broadcasting " + envState + " to "
Jeff Sharkey48877892015-03-18 11:27:19 -0700590 + userVol.getOwner());
591
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700592 final String action = VolumeInfo.getBroadcastForEnvironment(envState);
Jeff Sharkey48877892015-03-18 11:27:19 -0700593 if (action != null) {
594 final Intent intent = new Intent(action,
595 Uri.fromFile(userVol.getPathFile()));
596 intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, userVol);
597 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
598 mContext.sendBroadcastAsUser(intent, userVol.getOwner());
599 }
600 break;
601 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800602 }
603 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700604 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700605
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700606 private final Handler mHandler;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800607
Jeff Sharkey56e62932015-03-21 20:41:00 -0700608 @Override
609 public void waitForAsecScan() {
Jeff Sharkey48877892015-03-18 11:27:19 -0700610 waitForLatch(mAsecsScanned, "mAsecsScanned");
Kenny Root51a573c2012-05-17 13:30:28 -0700611 }
612
San Mehat207e5382010-02-04 20:46:54 -0800613 private void waitForReady() {
Jeff Sharkey48877892015-03-18 11:27:19 -0700614 waitForLatch(mConnectedSignal, "mConnectedSignal");
Kenny Root51a573c2012-05-17 13:30:28 -0700615 }
616
Jeff Sharkey48877892015-03-18 11:27:19 -0700617 private void waitForLatch(CountDownLatch latch, String condition) {
Kenny Root51a573c2012-05-17 13:30:28 -0700618 for (;;) {
619 try {
620 if (latch.await(5000, TimeUnit.MILLISECONDS)) {
San Mehat207e5382010-02-04 20:46:54 -0800621 return;
Kenny Root51a573c2012-05-17 13:30:28 -0700622 } else {
623 Slog.w(TAG, "Thread " + Thread.currentThread().getName()
Jeff Sharkey48877892015-03-18 11:27:19 -0700624 + " still waiting for " + condition + "...");
San Mehat207e5382010-02-04 20:46:54 -0800625 }
Kenny Root51a573c2012-05-17 13:30:28 -0700626 } catch (InterruptedException e) {
627 Slog.w(TAG, "Interrupt while waiting for MountService to be ready.");
San Mehat207e5382010-02-04 20:46:54 -0800628 }
San Mehat207e5382010-02-04 20:46:54 -0800629 }
San Mehat1f6301e2010-01-07 22:40:27 -0800630 }
Kenny Root02c87302010-07-01 08:10:18 -0700631
Paul Lawrence945490c2014-03-27 16:37:28 +0000632 private boolean isReady() {
633 try {
634 return mConnectedSignal.await(0, TimeUnit.MILLISECONDS);
635 } catch (InterruptedException e) {
636 return false;
637 }
638 }
639
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700640 private void handleSystemReady() {
Jeff Sharkey48877892015-03-18 11:27:19 -0700641 resetIfReadyAndConnected();
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700642
Jeff Sharkey48877892015-03-18 11:27:19 -0700643 // Start scheduling nominally-daily fstrim operations
Christopher Tate115afda2014-06-06 19:06:26 -0700644 MountServiceIdler.scheduleIdlePass(mContext);
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700645 }
646
Jeff Sharkey48877892015-03-18 11:27:19 -0700647 private void resetIfReadyAndConnected() {
648 Slog.d(TAG, "Thinking about reset, mSystemReady=" + mSystemReady
649 + ", mDaemonConnected=" + mDaemonConnected);
650 if (mSystemReady && mDaemonConnected) {
651 mDisks.clear();
652 mVolumes.clear();
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700653
Jeff Sharkey59d577a2015-04-11 21:27:21 -0700654 // Create a stub volume that represents internal storage
655 final VolumeInfo internal = new VolumeInfo(VolumeInfo.ID_PRIVATE_INTERNAL,
656 VolumeInfo.TYPE_PRIVATE, 0);
657 internal.state = VolumeInfo.STATE_MOUNTED;
658 internal.path = Environment.getDataDirectory().getAbsolutePath();
659 mVolumes.put(internal.id, internal);
660
Jeff Sharkey48877892015-03-18 11:27:19 -0700661 try {
662 mConnector.execute("volume", "reset");
663 } catch (NativeDaemonConnectorException e) {
664 Slog.w(TAG, "Failed to reset vold", e);
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700665 }
666 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700667 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700668
Jeff Sharkey48877892015-03-18 11:27:19 -0700669 private void onStartUser(int userId) {
670 Slog.d(TAG, "onStartUser " + userId);
671
672 // We purposefully block here to make sure that user-specific
673 // staging area is ready so it's ready for zygote-forked apps to
674 // bind mount against.
675 try {
676 mConnector.execute("volume", "start_user", userId);
677 } catch (NativeDaemonConnectorException ignored) {
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700678 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700679
680 // Record user as started so newly mounted volumes kick off events
681 // correctly, then synthesize events for any already-mounted volumes.
682 synchronized (mVolumes) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700683 for (int i = 0; i < mVolumes.size(); i++) {
684 final VolumeInfo vol = mVolumes.valueAt(i);
685 if (vol.isVisibleToUser(userId) && vol.state == VolumeInfo.STATE_MOUNTED) {
686 final StorageVolume userVol = vol.buildStorageVolume(mContext, userId);
Jeff Sharkey48877892015-03-18 11:27:19 -0700687 mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget();
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700688
689 mCallbacks.notifyStorageStateChanged(userVol.getPath(),
690 Environment.MEDIA_MOUNTED, Environment.MEDIA_MOUNTED);
Jeff Sharkey48877892015-03-18 11:27:19 -0700691 }
692 }
693 mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userId);
694 }
695 }
696
697 private void onCleanupUser(int userId) {
698 Slog.d(TAG, "onCleanupUser " + userId);
699
700 try {
701 mConnector.execute("volume", "cleanup_user", userId);
702 } catch (NativeDaemonConnectorException ignored) {
703 }
704
705 synchronized (mVolumes) {
706 mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userId);
707 }
708 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700709
Christopher Tated417d622013-08-19 16:14:25 -0700710 void runIdleMaintenance(Runnable callback) {
711 mHandler.sendMessage(mHandler.obtainMessage(H_FSTRIM, callback));
712 }
713
Christopher Tate7265abe2014-11-21 13:54:45 -0800714 // Binder entry point for kicking off an immediate fstrim
715 @Override
716 public void runMaintenance() {
Jeff Sharkey48877892015-03-18 11:27:19 -0700717 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
Christopher Tate7265abe2014-11-21 13:54:45 -0800718 runIdleMaintenance(null);
719 }
720
721 @Override
722 public long lastMaintenance() {
723 return mLastMaintenance;
724 }
725
San Mehat4270e1e2010-01-29 05:32:19 -0800726 /**
San Mehat4270e1e2010-01-29 05:32:19 -0800727 * Callback from NativeDaemonConnector
728 */
Jeff Sharkey48877892015-03-18 11:27:19 -0700729 @Override
San Mehat4270e1e2010-01-29 05:32:19 -0800730 public void onDaemonConnected() {
Jeff Sharkey48877892015-03-18 11:27:19 -0700731 mDaemonConnected = true;
732 mHandler.obtainMessage(H_DAEMON_CONNECTED).sendToTarget();
733 }
734
735 private void handleDaemonConnected() {
736 resetIfReadyAndConnected();
737
San Mehat4270e1e2010-01-29 05:32:19 -0800738 /*
Jeff Sharkey48877892015-03-18 11:27:19 -0700739 * Now that we've done our initialization, release
740 * the hounds!
San Mehat4270e1e2010-01-29 05:32:19 -0800741 */
Jeff Sharkey48877892015-03-18 11:27:19 -0700742 mConnectedSignal.countDown();
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400743
Jeff Sharkey48877892015-03-18 11:27:19 -0700744 // On an encrypted device we can't see system properties yet, so pull
745 // the system locale out of the mount service.
746 if ("".equals(SystemProperties.get("vold.encrypt_progress"))) {
747 copyLocaleFromMountService();
748 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700749
Jeff Sharkey48877892015-03-18 11:27:19 -0700750 // Let package manager load internal ASECs.
751 mPms.scanAvailableAsecs();
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400752
Jeff Sharkey48877892015-03-18 11:27:19 -0700753 // Notify people waiting for ASECs to be scanned that it's done.
754 mAsecsScanned.countDown();
San Mehat4270e1e2010-01-29 05:32:19 -0800755 }
756
Elliott Hughesf839b4f2014-09-26 12:30:47 -0700757 private void copyLocaleFromMountService() {
758 String systemLocale;
759 try {
760 systemLocale = getField(StorageManager.SYSTEM_LOCALE_KEY);
761 } catch (RemoteException e) {
762 return;
763 }
764 if (TextUtils.isEmpty(systemLocale)) {
765 return;
766 }
767
768 Slog.d(TAG, "Got locale " + systemLocale + " from mount service");
769 Locale locale = Locale.forLanguageTag(systemLocale);
770 Configuration config = new Configuration();
771 config.setLocale(locale);
772 try {
773 ActivityManagerNative.getDefault().updateConfiguration(config);
774 } catch (RemoteException e) {
775 Slog.e(TAG, "Error setting system locale from mount service", e);
776 }
Elliott Hughes9c33f282014-10-13 12:39:56 -0700777
778 // Temporary workaround for http://b/17945169.
779 Slog.d(TAG, "Setting system properties to " + systemLocale + " from mount service");
Narayan Kamathd30dbb82015-01-15 14:48:15 +0000780 SystemProperties.set("persist.sys.locale", locale.toLanguageTag());
Elliott Hughesf839b4f2014-09-26 12:30:47 -0700781 }
782
San Mehat4270e1e2010-01-29 05:32:19 -0800783 /**
San Mehat4270e1e2010-01-29 05:32:19 -0800784 * Callback from NativeDaemonConnector
785 */
Jeff Sharkey48877892015-03-18 11:27:19 -0700786 @Override
Dianne Hackborn77b987f2014-02-26 16:20:52 -0800787 public boolean onCheckHoldWakeLock(int code) {
788 return false;
789 }
790
791 /**
792 * Callback from NativeDaemonConnector
793 */
Jeff Sharkey48877892015-03-18 11:27:19 -0700794 @Override
San Mehat4270e1e2010-01-29 05:32:19 -0800795 public boolean onEvent(int code, String raw, String[] cooked) {
Jeff Sharkey48877892015-03-18 11:27:19 -0700796 synchronized (mLock) {
797 return onEventLocked(code, raw, cooked);
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800798 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700799 }
Jeff Sharkey5aca2b82013-10-16 16:21:54 -0700800
Jeff Sharkey48877892015-03-18 11:27:19 -0700801 private boolean onEventLocked(int code, String raw, String[] cooked) {
802 switch (code) {
803 case VoldResponseCode.DISK_CREATED: {
804 if (cooked.length != 3) break;
805 final String id = cooked[1];
806 final int flags = Integer.parseInt(cooked[2]);
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700807 mDisks.put(id, new DiskInfo(id, flags));
Jeff Sharkey48877892015-03-18 11:27:19 -0700808 break;
Jeff Sharkey5aca2b82013-10-16 16:21:54 -0700809 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700810 case VoldResponseCode.DISK_SIZE_CHANGED: {
811 if (cooked.length != 3) break;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700812 final DiskInfo disk = mDisks.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -0700813 if (disk != null) {
814 disk.size = Long.parseLong(cooked[2]);
San Mehat4270e1e2010-01-29 05:32:19 -0800815 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700816 break;
817 }
818 case VoldResponseCode.DISK_LABEL_CHANGED: {
819 if (cooked.length != 3) break;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700820 final DiskInfo disk = mDisks.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -0700821 if (disk != null) {
822 disk.label = cooked[2];
823 }
824 break;
825 }
826 case VoldResponseCode.DISK_VOLUME_CREATED: {
827 if (cooked.length != 3) break;
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700828 final String diskId = cooked[1];
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700829 final String volId = cooked[2];
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700830 final DiskInfo disk = mDisks.get(diskId);
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700831 if (disk != null) {
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700832 disk.volumeIds = ArrayUtils.appendElement(String.class, disk.volumeIds, volId);
833 }
834 final VolumeInfo vol = mVolumes.get(volId);
835 if (vol != null) {
836 vol.diskId = diskId;
Jeff Sharkey48877892015-03-18 11:27:19 -0700837 }
838 break;
839 }
Jeff Sharkey59d577a2015-04-11 21:27:21 -0700840 case VoldResponseCode.DISK_VOLUME_DESTROYED: {
841 if (cooked.length != 3) break;
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700842 final String diskId = cooked[1];
Jeff Sharkey59d577a2015-04-11 21:27:21 -0700843 final String volId = cooked[2];
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700844 final DiskInfo disk = mDisks.get(diskId);
Jeff Sharkey59d577a2015-04-11 21:27:21 -0700845 if (disk != null) {
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700846 disk.volumeIds = ArrayUtils.removeElement(String.class, disk.volumeIds, volId);
847 }
848 final VolumeInfo vol = mVolumes.get(volId);
849 if (vol != null) {
850 vol.diskId = null;
Jeff Sharkey59d577a2015-04-11 21:27:21 -0700851 }
852 break;
853 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700854 case VoldResponseCode.DISK_DESTROYED: {
855 if (cooked.length != 2) break;
856 mDisks.remove(cooked[1]);
857 break;
858 }
San Mehat4270e1e2010-01-29 05:32:19 -0800859
Jeff Sharkey48877892015-03-18 11:27:19 -0700860 case VoldResponseCode.VOLUME_CREATED: {
861 if (cooked.length != 3) break;
862 final String id = cooked[1];
863 final int type = Integer.parseInt(cooked[2]);
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700864 final int mtpIndex = allocateMtpIndex(id);
865 final VolumeInfo vol = new VolumeInfo(id, type, mtpIndex);
Jeff Sharkey48877892015-03-18 11:27:19 -0700866 mVolumes.put(id, vol);
867 onVolumeCreatedLocked(vol);
868 break;
869 }
870 case VoldResponseCode.VOLUME_STATE_CHANGED: {
871 if (cooked.length != 3) break;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700872 final VolumeInfo vol = mVolumes.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -0700873 if (vol != null) {
874 final int oldState = vol.state;
875 final int newState = Integer.parseInt(cooked[2]);
876 vol.state = newState;
877 onVolumeStateChangedLocked(vol, oldState, newState);
878 }
879 break;
880 }
881 case VoldResponseCode.VOLUME_FS_TYPE_CHANGED: {
882 if (cooked.length != 3) break;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700883 final VolumeInfo vol = mVolumes.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -0700884 if (vol != null) {
885 vol.fsType = cooked[2];
886 }
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700887 mCallbacks.notifyVolumeMetadataChanged(vol.clone());
Jeff Sharkey48877892015-03-18 11:27:19 -0700888 break;
889 }
890 case VoldResponseCode.VOLUME_FS_UUID_CHANGED: {
891 if (cooked.length != 3) break;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700892 final VolumeInfo vol = mVolumes.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -0700893 if (vol != null) {
894 vol.fsUuid = cooked[2];
895 }
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700896 refreshMetadataLocked();
897 mCallbacks.notifyVolumeMetadataChanged(vol.clone());
Jeff Sharkey48877892015-03-18 11:27:19 -0700898 break;
899 }
900 case VoldResponseCode.VOLUME_FS_LABEL_CHANGED: {
901 if (cooked.length != 3) break;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700902 final VolumeInfo vol = mVolumes.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -0700903 if (vol != null) {
904 vol.fsLabel = cooked[2];
905 }
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700906 mCallbacks.notifyVolumeMetadataChanged(vol.clone());
Jeff Sharkey48877892015-03-18 11:27:19 -0700907 break;
908 }
909 case VoldResponseCode.VOLUME_PATH_CHANGED: {
910 if (cooked.length != 3) break;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700911 final VolumeInfo vol = mVolumes.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -0700912 if (vol != null) {
913 vol.path = cooked[2];
914 }
915 break;
916 }
917 case VoldResponseCode.VOLUME_DESTROYED: {
918 if (cooked.length != 2) break;
919 mVolumes.remove(cooked[1]);
920 break;
921 }
San Mehat4270e1e2010-01-29 05:32:19 -0800922
Jeff Sharkey48877892015-03-18 11:27:19 -0700923 case VoldResponseCode.FstrimCompleted: {
Svetoslav9e814a82013-04-30 10:43:56 -0700924 EventLogTags.writeFstrimFinish(SystemClock.elapsedRealtime());
Jeff Sharkey48877892015-03-18 11:27:19 -0700925 break;
San Mehat4270e1e2010-01-29 05:32:19 -0800926 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700927 default: {
928 Slog.d(TAG, "Unhandled vold event " + code);
Mike Lockwooda5250c92011-05-23 13:44:04 -0400929 }
San Mehat4270e1e2010-01-29 05:32:19 -0800930 }
931
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400932 return true;
San Mehat4270e1e2010-01-29 05:32:19 -0800933 }
934
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700935 private void onVolumeCreatedLocked(VolumeInfo vol) {
936 final boolean primaryPhysical = SystemProperties.getBoolean(
937 StorageManager.PROP_PRIMARY_PHYSICAL, false);
Jeff Sharkey59d577a2015-04-11 21:27:21 -0700938 // TODO: enable switching to another emulated primary
939 if (VolumeInfo.ID_EMULATED_INTERNAL.equals(vol.id) && !primaryPhysical) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700940 vol.flags |= VolumeInfo.FLAG_PRIMARY;
941 vol.flags |= VolumeInfo.FLAG_VISIBLE;
Jeff Sharkey48877892015-03-18 11:27:19 -0700942 mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700943
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700944 } else if (vol.type == VolumeInfo.TYPE_PUBLIC) {
Jeff Sharkey48877892015-03-18 11:27:19 -0700945 if (primaryPhysical) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700946 vol.flags |= VolumeInfo.FLAG_PRIMARY;
San Mehat4270e1e2010-01-29 05:32:19 -0800947 }
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700948 vol.flags |= VolumeInfo.FLAG_VISIBLE;
Jeff Sharkey48877892015-03-18 11:27:19 -0700949 vol.userId = UserHandle.USER_OWNER;
950 mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
San Mehat4270e1e2010-01-29 05:32:19 -0800951
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -0700952 } else if (vol.type == VolumeInfo.TYPE_PRIVATE) {
953 mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
954
San Mehat4270e1e2010-01-29 05:32:19 -0800955 } else {
Jeff Sharkey48877892015-03-18 11:27:19 -0700956 Slog.d(TAG, "Skipping automatic mounting of " + vol);
San Mehat4270e1e2010-01-29 05:32:19 -0800957 }
958 }
959
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700960 private void onVolumeStateChangedLocked(VolumeInfo vol, int oldState, int newState) {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700961 mCallbacks.notifyVolumeStateChanged(vol.clone(), oldState, newState);
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700962
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700963 final String oldStateEnv = VolumeInfo.getEnvironmentForState(oldState);
964 final String newStateEnv = VolumeInfo.getEnvironmentForState(newState);
Emily Bernier92aa5a22014-07-07 10:11:48 -0400965
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700966 if (!Objects.equals(oldStateEnv, newStateEnv)) {
967 // Kick state changed event towards all started users. Any users
968 // started after this point will trigger additional
969 // user-specific broadcasts.
970 for (int userId : mStartedUsers) {
971 if (vol.isVisibleToUser(userId)) {
972 final StorageVolume userVol = vol.buildStorageVolume(mContext, userId);
973 mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget();
Jeff Sharkey48877892015-03-18 11:27:19 -0700974
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700975 mCallbacks.notifyStorageStateChanged(userVol.getPath(), oldStateEnv,
976 newStateEnv);
San Mehat4270e1e2010-01-29 05:32:19 -0800977 }
978 }
979 }
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700980
981 if (vol.type == VolumeInfo.TYPE_PUBLIC && vol.state == VolumeInfo.STATE_UNMOUNTING) {
982 // TODO: this should eventually be handled by new ObbVolume state changes
983 /*
984 * Some OBBs might have been unmounted when this volume was
985 * unmounted, so send a message to the handler to let it know to
986 * remove those from the list of mounted OBBS.
987 */
988 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(
989 OBB_FLUSH_MOUNT_STATE, vol.path));
990 }
San Mehat4270e1e2010-01-29 05:32:19 -0800991 }
992
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700993 /**
994 * Refresh latest metadata into any currently active {@link VolumeInfo}.
995 */
996 private void refreshMetadataLocked() {
997 final int size = mVolumes.size();
998 for (int i = 0; i < size; i++) {
999 final VolumeInfo vol = mVolumes.valueAt(i);
1000 final VolumeMetadata meta = mMetadata.get(vol.fsUuid);
1001
1002 if (meta != null) {
1003 vol.nickname = meta.nickname;
1004 vol.userFlags = meta.userFlags;
1005 } else {
1006 vol.nickname = null;
1007 vol.userFlags = 0;
1008 }
1009 }
1010 }
1011
Jeff Sharkey48877892015-03-18 11:27:19 -07001012 private void enforcePermission(String perm) {
1013 mContext.enforceCallingOrSelfPermission(perm, perm);
Mike Lockwooda5250c92011-05-23 13:44:04 -04001014 }
1015
Jeff Sharkey48877892015-03-18 11:27:19 -07001016 private void enforceUserRestriction(String restriction) {
Emily Bernier92aa5a22014-07-07 10:11:48 -04001017 UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
Jeff Sharkey48877892015-03-18 11:27:19 -07001018 if (um.hasUserRestriction(restriction, Binder.getCallingUserHandle())) {
Emily Bernier92aa5a22014-07-07 10:11:48 -04001019 throw new SecurityException("User has restriction " + restriction);
1020 }
1021 }
1022
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001023 /**
San Mehat207e5382010-02-04 20:46:54 -08001024 * Constructs a new MountService instance
1025 *
1026 * @param context Binder context for this service
1027 */
1028 public MountService(Context context) {
Christopher Tated417d622013-08-19 16:14:25 -07001029 sSelf = this;
1030
San Mehat207e5382010-02-04 20:46:54 -08001031 mContext = context;
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001032 mCallbacks = new Callbacks(FgThread.get().getLooper());
San Mehat207e5382010-02-04 20:46:54 -08001033
San Mehat207e5382010-02-04 20:46:54 -08001034 // XXX: This will go away soon in favor of IMountServiceObserver
1035 mPms = (PackageManagerService) ServiceManager.getService("package");
1036
Dianne Hackbornefa92b22013-05-03 14:11:43 -07001037 HandlerThread hthread = new HandlerThread(TAG);
1038 hthread.start();
1039 mHandler = new MountServiceHandler(hthread.getLooper());
Daniel Sandler5f27ef42010-03-16 15:42:02 -04001040
Kenny Roota02b8b02010-08-05 16:14:17 -07001041 // Add OBB Action Handler to MountService thread.
Dianne Hackborn8d044e82013-04-30 17:24:15 -07001042 mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper());
Kenny Roota02b8b02010-08-05 16:14:17 -07001043
Christopher Tate7265abe2014-11-21 13:54:45 -08001044 // Initialize the last-fstrim tracking if necessary
1045 File dataDir = Environment.getDataDirectory();
1046 File systemDir = new File(dataDir, "system");
1047 mLastMaintenanceFile = new File(systemDir, LAST_FSTRIM_FILE);
1048 if (!mLastMaintenanceFile.exists()) {
1049 // Not setting mLastMaintenance here means that we will force an
1050 // fstrim during reboot following the OTA that installs this code.
1051 try {
1052 (new FileOutputStream(mLastMaintenanceFile)).close();
1053 } catch (IOException e) {
1054 Slog.e(TAG, "Unable to create fstrim record " + mLastMaintenanceFile.getPath());
1055 }
1056 } else {
1057 mLastMaintenance = mLastMaintenanceFile.lastModified();
1058 }
1059
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001060 mMetadataFile = new AtomicFile(
1061 new File(Environment.getSystemSecureDirectory(), "storage.xml"));
1062
1063 synchronized (mLock) {
1064 readMetadataLocked();
1065 }
1066
Marco Nelissenc34ebce2010-02-18 13:39:41 -08001067 /*
Kenny Root305bcbf2010-09-03 07:56:38 -07001068 * Create the connection to vold with a maximum queue of twice the
1069 * amount of containers we'd ever expect to have. This keeps an
1070 * "asec list" from blocking a thread repeatedly.
1071 */
Dianne Hackborn77b987f2014-02-26 16:20:52 -08001072 mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25,
1073 null);
Jeff Sharkey48877892015-03-18 11:27:19 -07001074 mConnector.setDebug(true);
Kenny Root51a573c2012-05-17 13:30:28 -07001075
Kenny Root305bcbf2010-09-03 07:56:38 -07001076 Thread thread = new Thread(mConnector, VOLD_TAG);
San Mehat207e5382010-02-04 20:46:54 -08001077 thread.start();
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -07001078
Kenny Root07714d42011-08-17 17:49:28 -07001079 // Add ourself to the Watchdog monitors if enabled.
1080 if (WATCHDOG_ENABLE) {
1081 Watchdog.getInstance().addMonitor(this);
1082 }
San Mehat207e5382010-02-04 20:46:54 -08001083 }
1084
Jeff Sharkey56e62932015-03-21 20:41:00 -07001085 private void systemReady() {
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001086 mSystemReady = true;
1087 mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget();
1088 }
1089
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001090 private void readMetadataLocked() {
1091 mMetadata.clear();
1092
1093 FileInputStream fis = null;
1094 try {
1095 fis = mMetadataFile.openRead();
1096 final XmlPullParser in = Xml.newPullParser();
1097 in.setInput(fis, null);
1098
1099 int type;
1100 while ((type = in.next()) != END_DOCUMENT) {
1101 if (type == START_TAG) {
1102 final String tag = in.getName();
1103 if (TAG_VOLUME.equals(tag)) {
1104 final VolumeMetadata meta = VolumeMetadata.read(in);
1105 mMetadata.put(meta.fsUuid, meta);
1106 }
1107 }
1108 }
1109 } catch (FileNotFoundException e) {
1110 // Missing metadata is okay, probably first boot
1111 } catch (IOException e) {
1112 Slog.wtf(TAG, "Failed reading metadata", e);
1113 } catch (XmlPullParserException e) {
1114 Slog.wtf(TAG, "Failed reading metadata", e);
1115 } finally {
1116 IoUtils.closeQuietly(fis);
1117 }
1118 }
1119
1120 private void writeMetadataLocked() {
1121 FileOutputStream fos = null;
1122 try {
1123 fos = mMetadataFile.startWrite();
1124
1125 XmlSerializer out = new FastXmlSerializer();
1126 out.setOutput(fos, "utf-8");
1127 out.startDocument(null, true);
1128 out.startTag(null, TAG_VOLUMES);
1129 final int size = mMetadata.size();
1130 for (int i = 0; i < size; i++) {
1131 final VolumeMetadata meta = mMetadata.valueAt(i);
1132 VolumeMetadata.write(out, meta);
1133 }
1134 out.endTag(null, TAG_VOLUMES);
1135 out.endDocument();
1136
1137 mMetadataFile.finishWrite(fos);
1138 } catch (IOException e) {
1139 if (fos != null) {
1140 mMetadataFile.failWrite(fos);
1141 }
1142 }
1143 }
1144
San Mehat207e5382010-02-04 20:46:54 -08001145 /**
San Mehat4270e1e2010-01-29 05:32:19 -08001146 * Exposed API calls below here
1147 */
1148
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001149 @Override
San Mehat4270e1e2010-01-29 05:32:19 -08001150 public void registerListener(IMountServiceListener listener) {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001151 mCallbacks.register(listener);
San Mehat4270e1e2010-01-29 05:32:19 -08001152 }
1153
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001154 @Override
San Mehat4270e1e2010-01-29 05:32:19 -08001155 public void unregisterListener(IMountServiceListener listener) {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001156 mCallbacks.unregister(listener);
San Mehat4270e1e2010-01-29 05:32:19 -08001157 }
1158
Jeff Sharkey48877892015-03-18 11:27:19 -07001159 @Override
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -08001160 public void shutdown(final IMountShutdownObserver observer) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001161 enforcePermission(android.Manifest.permission.SHUTDOWN);
San Mehat4270e1e2010-01-29 05:32:19 -08001162
San Mehata5078592010-03-25 09:36:54 -07001163 Slog.i(TAG, "Shutting down");
Jeff Sharkey48877892015-03-18 11:27:19 -07001164 mHandler.obtainMessage(H_SHUTDOWN, observer).sendToTarget();
San Mehat4270e1e2010-01-29 05:32:19 -08001165 }
1166
Jeff Sharkey48877892015-03-18 11:27:19 -07001167 @Override
San Mehatb1043402010-02-05 08:26:50 -08001168 public boolean isUsbMassStorageConnected() {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001169 throw new UnsupportedOperationException();
San Mehatb1043402010-02-05 08:26:50 -08001170 }
1171
Jeff Sharkey48877892015-03-18 11:27:19 -07001172 @Override
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001173 public void setUsbMassStorageEnabled(boolean enable) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001174 throw new UnsupportedOperationException();
San Mehatb1043402010-02-05 08:26:50 -08001175 }
1176
Jeff Sharkey48877892015-03-18 11:27:19 -07001177 @Override
San Mehatb1043402010-02-05 08:26:50 -08001178 public boolean isUsbMassStorageEnabled() {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001179 throw new UnsupportedOperationException();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001180 }
Jason parks9ed98bc2011-01-17 09:58:35 -06001181
Jeff Sharkey48877892015-03-18 11:27:19 -07001182 @Override
San Mehat4270e1e2010-01-29 05:32:19 -08001183 public String getVolumeState(String mountPoint) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001184 throw new UnsupportedOperationException();
San Mehat7fd0fee2009-12-17 07:12:23 -08001185 }
1186
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001187 @Override
Kenny Roote1ff2142010-10-12 11:20:01 -07001188 public boolean isExternalStorageEmulated() {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001189 throw new UnsupportedOperationException();
Kenny Roote1ff2142010-10-12 11:20:01 -07001190 }
1191
Jeff Sharkey48877892015-03-18 11:27:19 -07001192 @Override
San Mehat4270e1e2010-01-29 05:32:19 -08001193 public int mountVolume(String path) {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001194 mount(findVolumeIdForPath(path));
1195 return 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001196 }
1197
Jeff Sharkey48877892015-03-18 11:27:19 -07001198 @Override
Ben Komalo13c71972011-09-07 16:35:56 -07001199 public void unmountVolume(String path, boolean force, boolean removeEncryption) {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001200 unmount(findVolumeIdForPath(path));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001201 }
1202
Jeff Sharkey48877892015-03-18 11:27:19 -07001203 @Override
San Mehat4270e1e2010-01-29 05:32:19 -08001204 public int formatVolume(String path) {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001205 format(findVolumeIdForPath(path));
1206 return 0;
1207 }
1208
1209 @Override
1210 public void mount(String volId) {
1211 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1212 waitForReady();
1213
1214 final VolumeInfo vol = findVolumeById(volId);
1215 if (vol.type == VolumeInfo.TYPE_PUBLIC || vol.type == VolumeInfo.TYPE_PRIVATE) {
1216 enforceUserRestriction(UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA);
1217 }
1218 try {
1219 mConnector.execute("volume", "mount", vol.id, vol.flags, vol.userId);
1220 } catch (NativeDaemonConnectorException e) {
1221 throw e.rethrowAsParcelableException();
1222 }
1223 }
1224
1225 @Override
1226 public void unmount(String volId) {
1227 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1228 waitForReady();
1229
1230 final VolumeInfo vol = findVolumeById(volId);
1231
1232 // TODO: expand PMS to know about multiple volumes
1233 if (vol.isPrimary()) {
1234 synchronized (mUnmountLock) {
1235 mUnmountSignal = new CountDownLatch(1);
1236 mPms.updateExternalMediaStatus(false, true);
1237 waitForLatch(mUnmountSignal, "mUnmountSignal");
1238 mUnmountSignal = null;
1239 }
1240 }
1241
1242 try {
1243 mConnector.execute("volume", "unmount", vol.id);
1244 } catch (NativeDaemonConnectorException e) {
1245 throw e.rethrowAsParcelableException();
1246 }
1247 }
1248
1249 @Override
1250 public void format(String volId) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001251 enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
San Mehat207e5382010-02-04 20:46:54 -08001252 waitForReady();
San Mehat5b77dab2010-01-26 13:28:50 -08001253
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001254 final VolumeInfo vol = findVolumeById(volId);
1255 try {
1256 mConnector.execute("volume", "format", vol.id);
1257 } catch (NativeDaemonConnectorException e) {
1258 throw e.rethrowAsParcelableException();
Jeff Sharkey48877892015-03-18 11:27:19 -07001259 }
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001260 }
1261
1262 @Override
1263 public void partitionPublic(String diskId) {
1264 enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
1265 waitForReady();
1266
1267 try {
1268 mConnector.execute("volume", "partition", diskId, "public");
1269 } catch (NativeDaemonConnectorException e) {
1270 throw e.rethrowAsParcelableException();
1271 }
1272 }
1273
1274 @Override
1275 public void partitionPrivate(String diskId) {
1276 enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
1277 waitForReady();
1278
1279 try {
1280 mConnector.execute("volume", "partition", diskId, "private");
1281 } catch (NativeDaemonConnectorException e) {
1282 throw e.rethrowAsParcelableException();
1283 }
1284 }
1285
1286 @Override
1287 public void partitionMixed(String diskId, int ratio) {
1288 enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
1289 waitForReady();
1290
1291 try {
1292 mConnector.execute("volume", "partition", diskId, "mixed", ratio);
1293 } catch (NativeDaemonConnectorException e) {
1294 throw e.rethrowAsParcelableException();
1295 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001296 }
1297
Jeff Sharkey48877892015-03-18 11:27:19 -07001298 @Override
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001299 public void setVolumeNickname(String volId, String nickname) {
1300 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1301 waitForReady();
1302
1303 synchronized (mLock) {
1304 final VolumeInfo vol = findVolumeById(volId);
1305 final VolumeMetadata meta = findOrCreateMetadataLocked(vol);
1306 meta.nickname = nickname;
1307 refreshMetadataLocked();
1308 writeMetadataLocked();
1309 mCallbacks.notifyVolumeMetadataChanged(vol.clone());
1310 }
1311 }
1312
1313 @Override
1314 public void setVolumeUserFlags(String volId, int flags, int mask) {
1315 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1316 waitForReady();
1317
1318 synchronized (mLock) {
1319 final VolumeInfo vol = findVolumeById(volId);
1320 final VolumeMetadata meta = findOrCreateMetadataLocked(vol);
1321 meta.userFlags = (meta.userFlags & ~mask) | (flags & mask);
1322 refreshMetadataLocked();
1323 writeMetadataLocked();
1324 mCallbacks.notifyVolumeMetadataChanged(vol.clone());
1325 }
1326 }
1327
1328 @Override
Mike Lockwoodecedfdc2011-06-08 15:11:59 -07001329 public int[] getStorageUsers(String path) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001330 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
San Mehatc1b4ce92010-02-16 17:13:03 -08001331 waitForReady();
1332 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001333 final String[] r = NativeDaemonEvent.filterMessageList(
1334 mConnector.executeForList("storage", "users", path),
1335 VoldResponseCode.StorageUsersListResult);
1336
San Mehatc1b4ce92010-02-16 17:13:03 -08001337 // FMT: <pid> <process name>
1338 int[] data = new int[r.length];
1339 for (int i = 0; i < r.length; i++) {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001340 String[] tok = r[i].split(" ");
San Mehatc1b4ce92010-02-16 17:13:03 -08001341 try {
1342 data[i] = Integer.parseInt(tok[0]);
1343 } catch (NumberFormatException nfe) {
San Mehata5078592010-03-25 09:36:54 -07001344 Slog.e(TAG, String.format("Error parsing pid %s", tok[0]));
San Mehatc1b4ce92010-02-16 17:13:03 -08001345 return new int[0];
1346 }
1347 }
1348 return data;
1349 } catch (NativeDaemonConnectorException e) {
San Mehata5078592010-03-25 09:36:54 -07001350 Slog.e(TAG, "Failed to retrieve storage users list", e);
San Mehatc1b4ce92010-02-16 17:13:03 -08001351 return new int[0];
1352 }
1353 }
1354
San Mehatb1043402010-02-05 08:26:50 -08001355 private void warnOnNotMounted() {
Jeff Sharkey48877892015-03-18 11:27:19 -07001356 synchronized (mLock) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001357 for (int i = 0; i < mVolumes.size(); i++) {
1358 final VolumeInfo vol = mVolumes.valueAt(i);
1359 if (vol.isPrimary() && vol.state == VolumeInfo.STATE_MOUNTED) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001360 // Cool beans, we have a mounted primary volume
1361 return;
1362 }
Jeff Sharkey32ee8312012-09-30 13:21:31 -07001363 }
San Mehatb1043402010-02-05 08:26:50 -08001364 }
Jeff Sharkey48877892015-03-18 11:27:19 -07001365
1366 Slog.w(TAG, "No primary storage mounted!");
San Mehatb1043402010-02-05 08:26:50 -08001367 }
1368
San Mehat4270e1e2010-01-29 05:32:19 -08001369 public String[] getSecureContainerList() {
Jeff Sharkey48877892015-03-18 11:27:19 -07001370 enforcePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat207e5382010-02-04 20:46:54 -08001371 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001372 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001373
San Mehat4270e1e2010-01-29 05:32:19 -08001374 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001375 return NativeDaemonEvent.filterMessageList(
1376 mConnector.executeForList("asec", "list"), VoldResponseCode.AsecListResult);
San Mehat4270e1e2010-01-29 05:32:19 -08001377 } catch (NativeDaemonConnectorException e) {
1378 return new String[0];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001379 }
1380 }
San Mehat36972292010-01-06 11:06:32 -08001381
Kenny Root6dceb882012-04-12 14:23:49 -07001382 public int createSecureContainer(String id, int sizeMb, String fstype, String key,
1383 int ownerUid, boolean external) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001384 enforcePermission(android.Manifest.permission.ASEC_CREATE);
San Mehat207e5382010-02-04 20:46:54 -08001385 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001386 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001387
San Mehatb1043402010-02-05 08:26:50 -08001388 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001389 try {
Jeff Sharkey56cd6462013-06-07 15:09:15 -07001390 mConnector.execute("asec", "create", id, sizeMb, fstype, new SensitiveArg(key),
1391 ownerUid, external ? "1" : "0");
San Mehat4270e1e2010-01-29 05:32:19 -08001392 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001393 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001394 }
San Mehata181b212010-02-11 06:50:20 -08001395
1396 if (rc == StorageResultCode.OperationSucceeded) {
1397 synchronized (mAsecMountSet) {
1398 mAsecMountSet.add(id);
1399 }
1400 }
San Mehat4270e1e2010-01-29 05:32:19 -08001401 return rc;
San Mehat36972292010-01-06 11:06:32 -08001402 }
1403
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001404 @Override
1405 public int resizeSecureContainer(String id, int sizeMb, String key) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001406 enforcePermission(android.Manifest.permission.ASEC_CREATE);
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001407 waitForReady();
1408 warnOnNotMounted();
1409
1410 int rc = StorageResultCode.OperationSucceeded;
1411 try {
1412 mConnector.execute("asec", "resize", id, sizeMb, new SensitiveArg(key));
1413 } catch (NativeDaemonConnectorException e) {
1414 rc = StorageResultCode.OperationFailedInternalError;
1415 }
1416 return rc;
1417 }
1418
San Mehat4270e1e2010-01-29 05:32:19 -08001419 public int finalizeSecureContainer(String id) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001420 enforcePermission(android.Manifest.permission.ASEC_CREATE);
San Mehatb1043402010-02-05 08:26:50 -08001421 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001422
San Mehatb1043402010-02-05 08:26:50 -08001423 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001424 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001425 mConnector.execute("asec", "finalize", id);
San Mehata181b212010-02-11 06:50:20 -08001426 /*
1427 * Finalization does a remount, so no need
1428 * to update mAsecMountSet
1429 */
San Mehat4270e1e2010-01-29 05:32:19 -08001430 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001431 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001432 }
San Mehat4270e1e2010-01-29 05:32:19 -08001433 return rc;
San Mehat36972292010-01-06 11:06:32 -08001434 }
1435
Kenny Root6dceb882012-04-12 14:23:49 -07001436 public int fixPermissionsSecureContainer(String id, int gid, String filename) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001437 enforcePermission(android.Manifest.permission.ASEC_CREATE);
Kenny Root6dceb882012-04-12 14:23:49 -07001438 warnOnNotMounted();
1439
1440 int rc = StorageResultCode.OperationSucceeded;
1441 try {
1442 mConnector.execute("asec", "fixperms", id, gid, filename);
1443 /*
1444 * Fix permissions does a remount, so no need to update
1445 * mAsecMountSet
1446 */
1447 } catch (NativeDaemonConnectorException e) {
1448 rc = StorageResultCode.OperationFailedInternalError;
1449 }
1450 return rc;
1451 }
1452
San Mehatd9709982010-02-18 11:43:03 -08001453 public int destroySecureContainer(String id, boolean force) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001454 enforcePermission(android.Manifest.permission.ASEC_DESTROY);
San Mehat207e5382010-02-04 20:46:54 -08001455 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001456 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001457
Kenny Rootaa485402010-09-14 14:49:41 -07001458 /*
1459 * Force a GC to make sure AssetManagers in other threads of the
1460 * system_server are cleaned up. We have to do this since AssetManager
1461 * instances are kept as a WeakReference and it's possible we have files
1462 * open on the external storage.
1463 */
1464 Runtime.getRuntime().gc();
1465
San Mehatb1043402010-02-05 08:26:50 -08001466 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001467 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001468 final Command cmd = new Command("asec", "destroy", id);
1469 if (force) {
1470 cmd.appendArg("force");
1471 }
1472 mConnector.execute(cmd);
San Mehat4270e1e2010-01-29 05:32:19 -08001473 } catch (NativeDaemonConnectorException e) {
San Mehatd9709982010-02-18 11:43:03 -08001474 int code = e.getCode();
1475 if (code == VoldResponseCode.OpFailedStorageBusy) {
1476 rc = StorageResultCode.OperationFailedStorageBusy;
1477 } else {
1478 rc = StorageResultCode.OperationFailedInternalError;
1479 }
San Mehat02735bc2010-01-26 15:18:08 -08001480 }
San Mehata181b212010-02-11 06:50:20 -08001481
1482 if (rc == StorageResultCode.OperationSucceeded) {
1483 synchronized (mAsecMountSet) {
1484 if (mAsecMountSet.contains(id)) {
1485 mAsecMountSet.remove(id);
1486 }
1487 }
1488 }
1489
San Mehat4270e1e2010-01-29 05:32:19 -08001490 return rc;
San Mehat36972292010-01-06 11:06:32 -08001491 }
Jason parks9ed98bc2011-01-17 09:58:35 -06001492
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001493 public int mountSecureContainer(String id, String key, int ownerUid, boolean readOnly) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001494 enforcePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
San Mehat207e5382010-02-04 20:46:54 -08001495 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001496 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001497
San Mehata181b212010-02-11 06:50:20 -08001498 synchronized (mAsecMountSet) {
1499 if (mAsecMountSet.contains(id)) {
1500 return StorageResultCode.OperationFailedStorageMounted;
1501 }
1502 }
1503
San Mehatb1043402010-02-05 08:26:50 -08001504 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001505 try {
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001506 mConnector.execute("asec", "mount", id, new SensitiveArg(key), ownerUid,
1507 readOnly ? "ro" : "rw");
San Mehat4270e1e2010-01-29 05:32:19 -08001508 } catch (NativeDaemonConnectorException e) {
Kenny Rootf0304622010-03-19 19:20:42 -07001509 int code = e.getCode();
1510 if (code != VoldResponseCode.OpFailedStorageBusy) {
1511 rc = StorageResultCode.OperationFailedInternalError;
1512 }
San Mehat02735bc2010-01-26 15:18:08 -08001513 }
San Mehat6cdd9c02010-02-09 14:45:20 -08001514
1515 if (rc == StorageResultCode.OperationSucceeded) {
1516 synchronized (mAsecMountSet) {
1517 mAsecMountSet.add(id);
1518 }
1519 }
San Mehat4270e1e2010-01-29 05:32:19 -08001520 return rc;
San Mehat36972292010-01-06 11:06:32 -08001521 }
1522
San Mehatd9709982010-02-18 11:43:03 -08001523 public int unmountSecureContainer(String id, boolean force) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001524 enforcePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
San Mehat207e5382010-02-04 20:46:54 -08001525 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001526 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001527
San Mehat6cdd9c02010-02-09 14:45:20 -08001528 synchronized (mAsecMountSet) {
1529 if (!mAsecMountSet.contains(id)) {
San Mehata181b212010-02-11 06:50:20 -08001530 return StorageResultCode.OperationFailedStorageNotMounted;
San Mehat6cdd9c02010-02-09 14:45:20 -08001531 }
1532 }
1533
Kenny Rootaa485402010-09-14 14:49:41 -07001534 /*
1535 * Force a GC to make sure AssetManagers in other threads of the
1536 * system_server are cleaned up. We have to do this since AssetManager
1537 * instances are kept as a WeakReference and it's possible we have files
1538 * open on the external storage.
1539 */
1540 Runtime.getRuntime().gc();
1541
San Mehatb1043402010-02-05 08:26:50 -08001542 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001543 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001544 final Command cmd = new Command("asec", "unmount", id);
1545 if (force) {
1546 cmd.appendArg("force");
1547 }
1548 mConnector.execute(cmd);
San Mehat4270e1e2010-01-29 05:32:19 -08001549 } catch (NativeDaemonConnectorException e) {
San Mehatd9709982010-02-18 11:43:03 -08001550 int code = e.getCode();
1551 if (code == VoldResponseCode.OpFailedStorageBusy) {
1552 rc = StorageResultCode.OperationFailedStorageBusy;
1553 } else {
1554 rc = StorageResultCode.OperationFailedInternalError;
1555 }
San Mehat02735bc2010-01-26 15:18:08 -08001556 }
San Mehat6cdd9c02010-02-09 14:45:20 -08001557
1558 if (rc == StorageResultCode.OperationSucceeded) {
1559 synchronized (mAsecMountSet) {
1560 mAsecMountSet.remove(id);
1561 }
1562 }
San Mehat4270e1e2010-01-29 05:32:19 -08001563 return rc;
San Mehat9dba7092010-01-18 06:47:41 -08001564 }
1565
San Mehat6cdd9c02010-02-09 14:45:20 -08001566 public boolean isSecureContainerMounted(String id) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001567 enforcePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat6cdd9c02010-02-09 14:45:20 -08001568 waitForReady();
1569 warnOnNotMounted();
1570
1571 synchronized (mAsecMountSet) {
1572 return mAsecMountSet.contains(id);
1573 }
1574 }
1575
San Mehat4270e1e2010-01-29 05:32:19 -08001576 public int renameSecureContainer(String oldId, String newId) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001577 enforcePermission(android.Manifest.permission.ASEC_RENAME);
San Mehat207e5382010-02-04 20:46:54 -08001578 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001579 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001580
San Mehata181b212010-02-11 06:50:20 -08001581 synchronized (mAsecMountSet) {
San Mehat85451ee2010-02-24 08:54:18 -08001582 /*
Jason parks9ed98bc2011-01-17 09:58:35 -06001583 * Because a mounted container has active internal state which cannot be
San Mehat85451ee2010-02-24 08:54:18 -08001584 * changed while active, we must ensure both ids are not currently mounted.
1585 */
1586 if (mAsecMountSet.contains(oldId) || mAsecMountSet.contains(newId)) {
San Mehata181b212010-02-11 06:50:20 -08001587 return StorageResultCode.OperationFailedStorageMounted;
1588 }
1589 }
1590
San Mehatb1043402010-02-05 08:26:50 -08001591 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001592 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001593 mConnector.execute("asec", "rename", oldId, newId);
San Mehat4270e1e2010-01-29 05:32:19 -08001594 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001595 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001596 }
San Mehata181b212010-02-11 06:50:20 -08001597
San Mehat4270e1e2010-01-29 05:32:19 -08001598 return rc;
San Mehat45f61042010-01-23 08:12:43 -08001599 }
1600
San Mehat4270e1e2010-01-29 05:32:19 -08001601 public String getSecureContainerPath(String id) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001602 enforcePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat207e5382010-02-04 20:46:54 -08001603 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001604 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001605
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001606 final NativeDaemonEvent event;
San Mehat2d66cef2010-03-23 11:12:52 -07001607 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001608 event = mConnector.execute("asec", "path", id);
1609 event.checkCode(VoldResponseCode.AsecPathResult);
1610 return event.getMessage();
San Mehat2d66cef2010-03-23 11:12:52 -07001611 } catch (NativeDaemonConnectorException e) {
1612 int code = e.getCode();
1613 if (code == VoldResponseCode.OpFailedStorageNotFound) {
Fredrik Helmera20c8ef2011-02-09 16:16:10 +01001614 Slog.i(TAG, String.format("Container '%s' not found", id));
1615 return null;
San Mehat22dd86e2010-01-12 12:21:18 -08001616 } else {
San Mehat2d66cef2010-03-23 11:12:52 -07001617 throw new IllegalStateException(String.format("Unexpected response code %d", code));
San Mehat22dd86e2010-01-12 12:21:18 -08001618 }
1619 }
San Mehat22dd86e2010-01-12 12:21:18 -08001620 }
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -07001621
Dianne Hackborn292f8bc2011-06-27 16:27:41 -07001622 public String getSecureContainerFilesystemPath(String id) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001623 enforcePermission(android.Manifest.permission.ASEC_ACCESS);
Dianne Hackborn292f8bc2011-06-27 16:27:41 -07001624 waitForReady();
1625 warnOnNotMounted();
1626
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001627 final NativeDaemonEvent event;
Dianne Hackborn292f8bc2011-06-27 16:27:41 -07001628 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001629 event = mConnector.execute("asec", "fspath", id);
1630 event.checkCode(VoldResponseCode.AsecPathResult);
1631 return event.getMessage();
Dianne Hackborn292f8bc2011-06-27 16:27:41 -07001632 } catch (NativeDaemonConnectorException e) {
1633 int code = e.getCode();
1634 if (code == VoldResponseCode.OpFailedStorageNotFound) {
1635 Slog.i(TAG, String.format("Container '%s' not found", id));
1636 return null;
1637 } else {
1638 throw new IllegalStateException(String.format("Unexpected response code %d", code));
1639 }
1640 }
1641 }
1642
Jeff Sharkey48877892015-03-18 11:27:19 -07001643 @Override
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -07001644 public void finishMediaUpdate() {
Jeff Sharkey48877892015-03-18 11:27:19 -07001645 if (mUnmountSignal != null) {
1646 mUnmountSignal.countDown();
1647 } else {
1648 Slog.w(TAG, "Odd, nobody asked to unmount?");
1649 }
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -07001650 }
Kenny Root02c87302010-07-01 08:10:18 -07001651
Kenny Roota02b8b02010-08-05 16:14:17 -07001652 private boolean isUidOwnerOfPackageOrSystem(String packageName, int callerUid) {
1653 if (callerUid == android.os.Process.SYSTEM_UID) {
1654 return true;
1655 }
1656
Kenny Root02c87302010-07-01 08:10:18 -07001657 if (packageName == null) {
1658 return false;
1659 }
1660
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07001661 final int packageUid = mPms.getPackageUid(packageName, UserHandle.getUserId(callerUid));
Kenny Root02c87302010-07-01 08:10:18 -07001662
1663 if (DEBUG_OBB) {
1664 Slog.d(TAG, "packageName = " + packageName + ", packageUid = " +
1665 packageUid + ", callerUid = " + callerUid);
1666 }
1667
1668 return callerUid == packageUid;
1669 }
1670
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001671 public String getMountedObbPath(String rawPath) {
1672 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
Kenny Rootaf9d6672010-10-08 09:21:39 -07001673
Kenny Root02c87302010-07-01 08:10:18 -07001674 waitForReady();
1675 warnOnNotMounted();
1676
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001677 final ObbState state;
1678 synchronized (mObbPathToStateMap) {
1679 state = mObbPathToStateMap.get(rawPath);
1680 }
1681 if (state == null) {
1682 Slog.w(TAG, "Failed to find OBB mounted at " + rawPath);
1683 return null;
1684 }
1685
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001686 final NativeDaemonEvent event;
Kenny Root02c87302010-07-01 08:10:18 -07001687 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001688 event = mConnector.execute("obb", "path", state.voldPath);
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001689 event.checkCode(VoldResponseCode.AsecPathResult);
1690 return event.getMessage();
Kenny Root02c87302010-07-01 08:10:18 -07001691 } catch (NativeDaemonConnectorException e) {
1692 int code = e.getCode();
1693 if (code == VoldResponseCode.OpFailedStorageNotFound) {
Kenny Roota02b8b02010-08-05 16:14:17 -07001694 return null;
Kenny Root02c87302010-07-01 08:10:18 -07001695 } else {
1696 throw new IllegalStateException(String.format("Unexpected response code %d", code));
1697 }
1698 }
1699 }
1700
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001701 @Override
1702 public boolean isObbMounted(String rawPath) {
1703 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
Kenny Rootaf9d6672010-10-08 09:21:39 -07001704 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001705 return mObbPathToStateMap.containsKey(rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07001706 }
Kenny Root02c87302010-07-01 08:10:18 -07001707 }
1708
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001709 @Override
1710 public void mountObb(
1711 String rawPath, String canonicalPath, String key, IObbActionListener token, int nonce) {
1712 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
1713 Preconditions.checkNotNull(canonicalPath, "canonicalPath cannot be null");
1714 Preconditions.checkNotNull(token, "token cannot be null");
Kenny Rootaf9d6672010-10-08 09:21:39 -07001715
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001716 final int callingUid = Binder.getCallingUid();
1717 final ObbState obbState = new ObbState(rawPath, canonicalPath, callingUid, token, nonce);
1718 final ObbAction action = new MountObbAction(obbState, key, callingUid);
Kenny Roota02b8b02010-08-05 16:14:17 -07001719 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
1720
1721 if (DEBUG_OBB)
1722 Slog.i(TAG, "Send to OBB handler: " + action.toString());
Kenny Root02c87302010-07-01 08:10:18 -07001723 }
1724
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001725 @Override
1726 public void unmountObb(String rawPath, boolean force, IObbActionListener token, int nonce) {
1727 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
1728
1729 final ObbState existingState;
1730 synchronized (mObbPathToStateMap) {
1731 existingState = mObbPathToStateMap.get(rawPath);
Kenny Rootf1121dc2010-09-29 07:30:53 -07001732 }
1733
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001734 if (existingState != null) {
1735 // TODO: separate state object from request data
1736 final int callingUid = Binder.getCallingUid();
1737 final ObbState newState = new ObbState(
1738 rawPath, existingState.canonicalPath, callingUid, token, nonce);
1739 final ObbAction action = new UnmountObbAction(newState, force);
1740 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
Kenny Root02c87302010-07-01 08:10:18 -07001741
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001742 if (DEBUG_OBB)
1743 Slog.i(TAG, "Send to OBB handler: " + action.toString());
1744 } else {
1745 Slog.w(TAG, "Unknown OBB mount at " + rawPath);
1746 }
Kenny Roota02b8b02010-08-05 16:14:17 -07001747 }
1748
Ben Komalo444eca22011-09-01 15:17:44 -07001749 @Override
1750 public int getEncryptionState() {
1751 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
1752 "no permission to access the crypt keeper");
1753
1754 waitForReady();
1755
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001756 final NativeDaemonEvent event;
Ben Komalo444eca22011-09-01 15:17:44 -07001757 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001758 event = mConnector.execute("cryptfs", "cryptocomplete");
1759 return Integer.parseInt(event.getMessage());
Ben Komalo444eca22011-09-01 15:17:44 -07001760 } catch (NumberFormatException e) {
1761 // Bad result - unexpected.
1762 Slog.w(TAG, "Unable to parse result from cryptfs cryptocomplete");
1763 return ENCRYPTION_STATE_ERROR_UNKNOWN;
1764 } catch (NativeDaemonConnectorException e) {
1765 // Something bad happened.
1766 Slog.w(TAG, "Error in communicating with cryptfs in validating");
1767 return ENCRYPTION_STATE_ERROR_UNKNOWN;
1768 }
1769 }
1770
Narayan Kamath1653b1d2014-12-17 13:40:36 +00001771 private static String toHex(String password) {
Paul Lawrence8e397362014-01-27 15:22:30 -08001772 if (password == null) {
Narayan Kamath78108a32014-12-16 12:56:23 +00001773 return "";
Paul Lawrence8e397362014-01-27 15:22:30 -08001774 }
1775 byte[] bytes = password.getBytes(StandardCharsets.UTF_8);
Narayan Kamath78108a32014-12-16 12:56:23 +00001776 return new String(HexEncoding.encode(bytes));
Paul Lawrence8e397362014-01-27 15:22:30 -08001777 }
1778
Narayan Kamath1653b1d2014-12-17 13:40:36 +00001779 private static String fromHex(String hexPassword) throws IllegalArgumentException {
Paul Lawrence945490c2014-03-27 16:37:28 +00001780 if (hexPassword == null) {
1781 return null;
1782 }
1783
Narayan Kamath1653b1d2014-12-17 13:40:36 +00001784 final byte[] bytes = HexEncoding.decode(hexPassword.toCharArray(), false);
Narayan Kamath78108a32014-12-16 12:56:23 +00001785 return new String(bytes, StandardCharsets.UTF_8);
Paul Lawrence945490c2014-03-27 16:37:28 +00001786 }
1787
Ben Komalo444eca22011-09-01 15:17:44 -07001788 @Override
Jason parks5af0b912010-11-29 09:05:25 -06001789 public int decryptStorage(String password) {
Jason parksf7b3cd42011-01-27 09:28:25 -06001790 if (TextUtils.isEmpty(password)) {
1791 throw new IllegalArgumentException("password cannot be empty");
Jason parks5af0b912010-11-29 09:05:25 -06001792 }
1793
Jason parks8888c592011-01-20 22:46:41 -06001794 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
1795 "no permission to access the crypt keeper");
Jason parks5af0b912010-11-29 09:05:25 -06001796
1797 waitForReady();
1798
1799 if (DEBUG_EVENTS) {
1800 Slog.i(TAG, "decrypting storage...");
1801 }
1802
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001803 final NativeDaemonEvent event;
Jason parks5af0b912010-11-29 09:05:25 -06001804 try {
Paul Lawrence8e397362014-01-27 15:22:30 -08001805 event = mConnector.execute("cryptfs", "checkpw", new SensitiveArg(toHex(password)));
Jason parks9ed98bc2011-01-17 09:58:35 -06001806
Fredrik Roubertda6aedf2011-12-20 17:34:43 +01001807 final int code = Integer.parseInt(event.getMessage());
Jason parks9ed98bc2011-01-17 09:58:35 -06001808 if (code == 0) {
1809 // Decrypt was successful. Post a delayed message before restarting in order
1810 // to let the UI to clear itself
1811 mHandler.postDelayed(new Runnable() {
1812 public void run() {
Jeff Sharkey31c6e482011-11-18 17:09:01 -08001813 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001814 mConnector.execute("cryptfs", "restart");
Jeff Sharkey31c6e482011-11-18 17:09:01 -08001815 } catch (NativeDaemonConnectorException e) {
1816 Slog.e(TAG, "problem executing in background", e);
1817 }
Jason parks9ed98bc2011-01-17 09:58:35 -06001818 }
Jason parksf7b3cd42011-01-27 09:28:25 -06001819 }, 1000); // 1 second
Jason parks9ed98bc2011-01-17 09:58:35 -06001820 }
1821
1822 return code;
Jason parks5af0b912010-11-29 09:05:25 -06001823 } catch (NativeDaemonConnectorException e) {
1824 // Decryption failed
1825 return e.getCode();
1826 }
Jason parks5af0b912010-11-29 09:05:25 -06001827 }
1828
Paul Lawrence46791e72014-04-03 09:10:26 -07001829 public int encryptStorage(int type, String password) {
1830 if (TextUtils.isEmpty(password) && type != StorageManager.CRYPT_TYPE_DEFAULT) {
Jason parksf7b3cd42011-01-27 09:28:25 -06001831 throw new IllegalArgumentException("password cannot be empty");
Jason parks56aa5322011-01-07 09:01:15 -06001832 }
1833
Jason parks8888c592011-01-20 22:46:41 -06001834 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
1835 "no permission to access the crypt keeper");
Jason parks56aa5322011-01-07 09:01:15 -06001836
1837 waitForReady();
1838
1839 if (DEBUG_EVENTS) {
Jason parks8888c592011-01-20 22:46:41 -06001840 Slog.i(TAG, "encrypting storage...");
Jason parks56aa5322011-01-07 09:01:15 -06001841 }
1842
1843 try {
Paul Lawrence46791e72014-04-03 09:10:26 -07001844 mConnector.execute("cryptfs", "enablecrypto", "inplace", CRYPTO_TYPES[type],
Paul Lawrence8e397362014-01-27 15:22:30 -08001845 new SensitiveArg(toHex(password)));
Jason parks56aa5322011-01-07 09:01:15 -06001846 } catch (NativeDaemonConnectorException e) {
1847 // Encryption failed
1848 return e.getCode();
1849 }
1850
1851 return 0;
1852 }
1853
Paul Lawrence8e397362014-01-27 15:22:30 -08001854 /** Set the password for encrypting the master key.
1855 * @param type One of the CRYPTO_TYPE_XXX consts defined in StorageManager.
1856 * @param password The password to set.
1857 */
1858 public int changeEncryptionPassword(int type, String password) {
Jason parksf7b3cd42011-01-27 09:28:25 -06001859 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
1860 "no permission to access the crypt keeper");
1861
1862 waitForReady();
1863
1864 if (DEBUG_EVENTS) {
1865 Slog.i(TAG, "changing encryption password...");
1866 }
1867
1868 try {
Svetoslava6711ff2014-10-17 11:38:06 -07001869 NativeDaemonEvent event = mConnector.execute("cryptfs", "changepw", CRYPTO_TYPES[type],
Svetoslav16e4a1a2014-09-29 18:16:20 -07001870 new SensitiveArg(toHex(password)));
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001871 return Integer.parseInt(event.getMessage());
Jason parksf7b3cd42011-01-27 09:28:25 -06001872 } catch (NativeDaemonConnectorException e) {
1873 // Encryption failed
1874 return e.getCode();
1875 }
1876 }
1877
Christopher Tate32418be2011-10-10 13:51:12 -07001878 /**
1879 * Validate a user-supplied password string with cryptfs
1880 */
1881 @Override
1882 public int verifyEncryptionPassword(String password) throws RemoteException {
1883 // Only the system process is permitted to validate passwords
1884 if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
1885 throw new SecurityException("no permission to access the crypt keeper");
1886 }
1887
1888 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
1889 "no permission to access the crypt keeper");
1890
1891 if (TextUtils.isEmpty(password)) {
1892 throw new IllegalArgumentException("password cannot be empty");
1893 }
1894
1895 waitForReady();
1896
1897 if (DEBUG_EVENTS) {
1898 Slog.i(TAG, "validating encryption password...");
1899 }
1900
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001901 final NativeDaemonEvent event;
Christopher Tate32418be2011-10-10 13:51:12 -07001902 try {
Paul Lawrence8e397362014-01-27 15:22:30 -08001903 event = mConnector.execute("cryptfs", "verifypw", new SensitiveArg(toHex(password)));
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001904 Slog.i(TAG, "cryptfs verifypw => " + event.getMessage());
1905 return Integer.parseInt(event.getMessage());
Christopher Tate32418be2011-10-10 13:51:12 -07001906 } catch (NativeDaemonConnectorException e) {
1907 // Encryption failed
1908 return e.getCode();
1909 }
1910 }
1911
Paul Lawrence8e397362014-01-27 15:22:30 -08001912 /**
1913 * Get the type of encryption used to encrypt the master key.
1914 * @return The type, one of the CRYPT_TYPE_XXX consts from StorageManager.
1915 */
1916 @Override
Svetoslav16e4a1a2014-09-29 18:16:20 -07001917 public int getPasswordType() {
Paul Lawrence8e397362014-01-27 15:22:30 -08001918
1919 waitForReady();
1920
1921 final NativeDaemonEvent event;
1922 try {
1923 event = mConnector.execute("cryptfs", "getpwtype");
1924 for (int i = 0; i < CRYPTO_TYPES.length; ++i) {
1925 if (CRYPTO_TYPES[i].equals(event.getMessage()))
1926 return i;
1927 }
1928
1929 throw new IllegalStateException("unexpected return from cryptfs");
1930 } catch (NativeDaemonConnectorException e) {
1931 throw e.rethrowAsParcelableException();
1932 }
1933 }
1934
Paul Lawrencee51dcf92014-03-18 10:56:00 -07001935 /**
1936 * Set a field in the crypto header.
1937 * @param field field to set
1938 * @param contents contents to set in field
1939 */
1940 @Override
1941 public void setField(String field, String contents) throws RemoteException {
1942
1943 waitForReady();
1944
1945 final NativeDaemonEvent event;
1946 try {
1947 event = mConnector.execute("cryptfs", "setfield", field, contents);
1948 } catch (NativeDaemonConnectorException e) {
1949 throw e.rethrowAsParcelableException();
1950 }
1951 }
1952
1953 /**
1954 * Gets a field from the crypto header.
1955 * @param field field to get
1956 * @return contents of field
1957 */
1958 @Override
1959 public String getField(String field) throws RemoteException {
1960
1961 waitForReady();
1962
1963 final NativeDaemonEvent event;
1964 try {
1965 final String[] contents = NativeDaemonEvent.filterMessageList(
1966 mConnector.executeForList("cryptfs", "getfield", field),
1967 VoldResponseCode.CryptfsGetfieldResult);
1968 String result = new String();
1969 for (String content : contents) {
1970 result += content;
1971 }
1972 return result;
1973 } catch (NativeDaemonConnectorException e) {
1974 throw e.rethrowAsParcelableException();
1975 }
1976 }
1977
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001978 @Override
Paul Lawrence945490c2014-03-27 16:37:28 +00001979 public String getPassword() throws RemoteException {
1980 if (!isReady()) {
1981 return new String();
1982 }
1983
1984 final NativeDaemonEvent event;
1985 try {
1986 event = mConnector.execute("cryptfs", "getpw");
Paul Lawrence24063b52015-01-06 13:11:23 -08001987 if ("-1".equals(event.getMessage())) {
1988 // -1 equals no password
1989 return null;
1990 }
Paul Lawrence945490c2014-03-27 16:37:28 +00001991 return fromHex(event.getMessage());
1992 } catch (NativeDaemonConnectorException e) {
1993 throw e.rethrowAsParcelableException();
Paul Lawrence24063b52015-01-06 13:11:23 -08001994 } catch (IllegalArgumentException e) {
1995 Slog.e(TAG, "Invalid response to getPassword");
1996 return null;
Paul Lawrence945490c2014-03-27 16:37:28 +00001997 }
1998 }
1999
2000 @Override
2001 public void clearPassword() throws RemoteException {
2002 if (!isReady()) {
2003 return;
2004 }
2005
2006 final NativeDaemonEvent event;
2007 try {
2008 event = mConnector.execute("cryptfs", "clearpw");
2009 } catch (NativeDaemonConnectorException e) {
2010 throw e.rethrowAsParcelableException();
2011 }
2012 }
2013
2014 @Override
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002015 public int mkdirs(String callingPkg, String appPath) {
2016 final int userId = UserHandle.getUserId(Binder.getCallingUid());
2017 final UserEnvironment userEnv = new UserEnvironment(userId);
2018
2019 // Validate that reported package name belongs to caller
2020 final AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(
2021 Context.APP_OPS_SERVICE);
2022 appOps.checkPackage(Binder.getCallingUid(), callingPkg);
2023
Jeff Sharkey48877892015-03-18 11:27:19 -07002024 File appFile = null;
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002025 try {
Jeff Sharkey48877892015-03-18 11:27:19 -07002026 appFile = new File(appPath).getCanonicalFile();
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002027 } catch (IOException e) {
2028 Slog.e(TAG, "Failed to resolve " + appPath + ": " + e);
2029 return -1;
2030 }
2031
2032 // Try translating the app path into a vold path, but require that it
2033 // belong to the calling package.
Jeff Sharkey48877892015-03-18 11:27:19 -07002034 if (FileUtils.contains(userEnv.buildExternalStorageAppDataDirs(callingPkg), appFile) ||
2035 FileUtils.contains(userEnv.buildExternalStorageAppObbDirs(callingPkg), appFile) ||
2036 FileUtils.contains(userEnv.buildExternalStorageAppMediaDirs(callingPkg), appFile)) {
2037 appPath = appFile.getAbsolutePath();
2038 if (!appPath.endsWith("/")) {
2039 appPath = appPath + "/";
2040 }
2041
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002042 try {
Jeff Sharkey48877892015-03-18 11:27:19 -07002043 mConnector.execute("volume", "mkdirs", appPath);
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002044 return 0;
2045 } catch (NativeDaemonConnectorException e) {
2046 return e.getCode();
2047 }
2048 }
2049
Jeff Sharkey48877892015-03-18 11:27:19 -07002050 throw new SecurityException("Invalid mkdirs path: " + appFile);
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002051 }
2052
2053 @Override
Jeff Sharkey48877892015-03-18 11:27:19 -07002054 public StorageVolume[] getVolumeList(int userId) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002055 final ArrayList<StorageVolume> res = new ArrayList<>();
Jeff Sharkey48877892015-03-18 11:27:19 -07002056 boolean foundPrimary = false;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002057
Jeff Sharkey48877892015-03-18 11:27:19 -07002058 synchronized (mLock) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002059 for (int i = 0; i < mVolumes.size(); i++) {
2060 final VolumeInfo vol = mVolumes.valueAt(i);
Jeff Sharkey48877892015-03-18 11:27:19 -07002061 if (vol.isVisibleToUser(userId)) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002062 final StorageVolume userVol = vol.buildStorageVolume(mContext, userId);
Jeff Sharkey48877892015-03-18 11:27:19 -07002063 if (vol.isPrimary()) {
2064 res.add(0, userVol);
2065 foundPrimary = true;
2066 } else {
2067 res.add(userVol);
2068 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -07002069 }
Mike Lockwood8fa5f802011-03-24 08:12:30 -07002070 }
Mike Lockwood8fa5f802011-03-24 08:12:30 -07002071 }
Jeff Sharkey48877892015-03-18 11:27:19 -07002072
2073 if (!foundPrimary) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002074 Log.w(TAG, "No primary storage defined yet; hacking together a stub");
Jeff Sharkey48877892015-03-18 11:27:19 -07002075
2076 final boolean primaryPhysical = SystemProperties.getBoolean(
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002077 StorageManager.PROP_PRIMARY_PHYSICAL, false);
Jeff Sharkey48877892015-03-18 11:27:19 -07002078
2079 final String id = "stub_primary";
2080 final File path = Environment.getLegacyExternalStorageDirectory();
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002081 final String description = mContext.getString(android.R.string.unknownName);
Jeff Sharkey48877892015-03-18 11:27:19 -07002082 final boolean primary = true;
2083 final boolean removable = primaryPhysical;
2084 final boolean emulated = !primaryPhysical;
2085 final long mtpReserveSize = 0L;
2086 final boolean allowMassStorage = false;
2087 final long maxFileSize = 0L;
2088 final UserHandle owner = new UserHandle(userId);
2089 final String uuid = null;
Jeff Sharkey48877892015-03-18 11:27:19 -07002090 final String state = Environment.MEDIA_REMOVED;
2091
2092 res.add(0, new StorageVolume(id, MtpStorage.getStorageIdForIndex(0), path,
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002093 description, primary, removable, emulated, mtpReserveSize,
2094 allowMassStorage, maxFileSize, owner, uuid, state));
Jeff Sharkey48877892015-03-18 11:27:19 -07002095 }
2096
2097 return res.toArray(new StorageVolume[res.size()]);
Mike Lockwood8fa5f802011-03-24 08:12:30 -07002098 }
2099
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002100 @Override
2101 public DiskInfo[] getDisks() {
2102 synchronized (mLock) {
2103 final DiskInfo[] res = new DiskInfo[mDisks.size()];
2104 for (int i = 0; i < mDisks.size(); i++) {
2105 res[i] = mDisks.valueAt(i);
2106 }
2107 return res;
2108 }
2109 }
2110
2111 @Override
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07002112 public VolumeInfo[] getVolumes(int flags) {
2113 if ((flags & StorageManager.FLAG_ALL_METADATA) != 0) {
2114 // TODO: implement support for returning all metadata
2115 throw new UnsupportedOperationException();
2116 }
2117
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002118 synchronized (mLock) {
2119 final VolumeInfo[] res = new VolumeInfo[mVolumes.size()];
2120 for (int i = 0; i < mVolumes.size(); i++) {
2121 res[i] = mVolumes.valueAt(i);
2122 }
2123 return res;
2124 }
2125 }
2126
Kenny Rootaf9d6672010-10-08 09:21:39 -07002127 private void addObbStateLocked(ObbState obbState) throws RemoteException {
2128 final IBinder binder = obbState.getBinder();
2129 List<ObbState> obbStates = mObbMounts.get(binder);
Kenny Root5919ac62010-10-05 09:49:40 -07002130
Kenny Rootaf9d6672010-10-08 09:21:39 -07002131 if (obbStates == null) {
2132 obbStates = new ArrayList<ObbState>();
2133 mObbMounts.put(binder, obbStates);
2134 } else {
2135 for (final ObbState o : obbStates) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002136 if (o.rawPath.equals(obbState.rawPath)) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002137 throw new IllegalStateException("Attempt to add ObbState twice. "
2138 + "This indicates an error in the MountService logic.");
Kenny Root5919ac62010-10-05 09:49:40 -07002139 }
2140 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002141 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07002142
2143 obbStates.add(obbState);
2144 try {
2145 obbState.link();
2146 } catch (RemoteException e) {
2147 /*
2148 * The binder died before we could link it, so clean up our state
2149 * and return failure.
2150 */
2151 obbStates.remove(obbState);
2152 if (obbStates.isEmpty()) {
2153 mObbMounts.remove(binder);
2154 }
2155
2156 // Rethrow the error so mountObb can get it
2157 throw e;
2158 }
2159
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002160 mObbPathToStateMap.put(obbState.rawPath, obbState);
Kenny Roota02b8b02010-08-05 16:14:17 -07002161 }
2162
Kenny Rootaf9d6672010-10-08 09:21:39 -07002163 private void removeObbStateLocked(ObbState obbState) {
2164 final IBinder binder = obbState.getBinder();
2165 final List<ObbState> obbStates = mObbMounts.get(binder);
2166 if (obbStates != null) {
2167 if (obbStates.remove(obbState)) {
2168 obbState.unlink();
Kenny Root05105f72010-09-22 17:29:43 -07002169 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07002170 if (obbStates.isEmpty()) {
2171 mObbMounts.remove(binder);
2172 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002173 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002174
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002175 mObbPathToStateMap.remove(obbState.rawPath);
Kenny Root38cf8862010-09-26 14:18:51 -07002176 }
2177
Kenny Roota02b8b02010-08-05 16:14:17 -07002178 private class ObbActionHandler extends Handler {
2179 private boolean mBound = false;
Kenny Root480afe72010-10-07 10:17:50 -07002180 private final List<ObbAction> mActions = new LinkedList<ObbAction>();
Kenny Roota02b8b02010-08-05 16:14:17 -07002181
2182 ObbActionHandler(Looper l) {
2183 super(l);
2184 }
2185
2186 @Override
2187 public void handleMessage(Message msg) {
2188 switch (msg.what) {
2189 case OBB_RUN_ACTION: {
Kenny Root480afe72010-10-07 10:17:50 -07002190 final ObbAction action = (ObbAction) msg.obj;
Kenny Roota02b8b02010-08-05 16:14:17 -07002191
2192 if (DEBUG_OBB)
2193 Slog.i(TAG, "OBB_RUN_ACTION: " + action.toString());
2194
2195 // If a bind was already initiated we don't really
2196 // need to do anything. The pending install
2197 // will be processed later on.
2198 if (!mBound) {
2199 // If this is the only one pending we might
2200 // have to bind to the service again.
2201 if (!connectToService()) {
2202 Slog.e(TAG, "Failed to bind to media container service");
2203 action.handleError();
2204 return;
Kenny Roota02b8b02010-08-05 16:14:17 -07002205 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002206 }
Kenny Root735de3b2010-09-30 14:11:39 -07002207
Kenny Root735de3b2010-09-30 14:11:39 -07002208 mActions.add(action);
Kenny Roota02b8b02010-08-05 16:14:17 -07002209 break;
2210 }
2211 case OBB_MCS_BOUND: {
2212 if (DEBUG_OBB)
2213 Slog.i(TAG, "OBB_MCS_BOUND");
2214 if (msg.obj != null) {
2215 mContainerService = (IMediaContainerService) msg.obj;
2216 }
2217 if (mContainerService == null) {
2218 // Something seriously wrong. Bail out
2219 Slog.e(TAG, "Cannot bind to media container service");
2220 for (ObbAction action : mActions) {
2221 // Indicate service bind error
2222 action.handleError();
2223 }
2224 mActions.clear();
2225 } else if (mActions.size() > 0) {
Kenny Root480afe72010-10-07 10:17:50 -07002226 final ObbAction action = mActions.get(0);
Kenny Roota02b8b02010-08-05 16:14:17 -07002227 if (action != null) {
2228 action.execute(this);
2229 }
2230 } else {
2231 // Should never happen ideally.
2232 Slog.w(TAG, "Empty queue");
2233 }
2234 break;
2235 }
2236 case OBB_MCS_RECONNECT: {
2237 if (DEBUG_OBB)
2238 Slog.i(TAG, "OBB_MCS_RECONNECT");
2239 if (mActions.size() > 0) {
2240 if (mBound) {
2241 disconnectService();
2242 }
2243 if (!connectToService()) {
2244 Slog.e(TAG, "Failed to bind to media container service");
2245 for (ObbAction action : mActions) {
2246 // Indicate service bind error
2247 action.handleError();
2248 }
2249 mActions.clear();
2250 }
2251 }
2252 break;
2253 }
2254 case OBB_MCS_UNBIND: {
2255 if (DEBUG_OBB)
2256 Slog.i(TAG, "OBB_MCS_UNBIND");
2257
2258 // Delete pending install
2259 if (mActions.size() > 0) {
2260 mActions.remove(0);
2261 }
2262 if (mActions.size() == 0) {
2263 if (mBound) {
2264 disconnectService();
2265 }
2266 } else {
2267 // There are more pending requests in queue.
2268 // Just post MCS_BOUND message to trigger processing
2269 // of next pending install.
2270 mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND);
2271 }
2272 break;
2273 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07002274 case OBB_FLUSH_MOUNT_STATE: {
2275 final String path = (String) msg.obj;
2276
2277 if (DEBUG_OBB)
2278 Slog.i(TAG, "Flushing all OBB state for path " + path);
2279
2280 synchronized (mObbMounts) {
2281 final List<ObbState> obbStatesToRemove = new LinkedList<ObbState>();
2282
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002283 final Iterator<ObbState> i = mObbPathToStateMap.values().iterator();
Kenny Rootaf9d6672010-10-08 09:21:39 -07002284 while (i.hasNext()) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002285 final ObbState state = i.next();
Kenny Rootaf9d6672010-10-08 09:21:39 -07002286
2287 /*
2288 * If this entry's source file is in the volume path
2289 * that got unmounted, remove it because it's no
2290 * longer valid.
2291 */
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002292 if (state.canonicalPath.startsWith(path)) {
2293 obbStatesToRemove.add(state);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002294 }
2295 }
2296
2297 for (final ObbState obbState : obbStatesToRemove) {
2298 if (DEBUG_OBB)
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002299 Slog.i(TAG, "Removing state for " + obbState.rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002300
2301 removeObbStateLocked(obbState);
2302
2303 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002304 obbState.token.onObbResult(obbState.rawPath, obbState.nonce,
Kenny Rootaf9d6672010-10-08 09:21:39 -07002305 OnObbStateChangeListener.UNMOUNTED);
2306 } catch (RemoteException e) {
2307 Slog.i(TAG, "Couldn't send unmount notification for OBB: "
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002308 + obbState.rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002309 }
2310 }
2311 }
2312 break;
2313 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002314 }
2315 }
2316
2317 private boolean connectToService() {
2318 if (DEBUG_OBB)
2319 Slog.i(TAG, "Trying to bind to DefaultContainerService");
2320
2321 Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
2322 if (mContext.bindService(service, mDefContainerConn, Context.BIND_AUTO_CREATE)) {
2323 mBound = true;
2324 return true;
2325 }
2326 return false;
2327 }
2328
2329 private void disconnectService() {
2330 mContainerService = null;
2331 mBound = false;
2332 mContext.unbindService(mDefContainerConn);
2333 }
2334 }
2335
2336 abstract class ObbAction {
2337 private static final int MAX_RETRIES = 3;
2338 private int mRetries;
2339
2340 ObbState mObbState;
2341
2342 ObbAction(ObbState obbState) {
2343 mObbState = obbState;
2344 }
2345
2346 public void execute(ObbActionHandler handler) {
2347 try {
2348 if (DEBUG_OBB)
Ben Komalo444eca22011-09-01 15:17:44 -07002349 Slog.i(TAG, "Starting to execute action: " + toString());
Kenny Roota02b8b02010-08-05 16:14:17 -07002350 mRetries++;
2351 if (mRetries > MAX_RETRIES) {
2352 Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
Kenny Root480afe72010-10-07 10:17:50 -07002353 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
Kenny Roota02b8b02010-08-05 16:14:17 -07002354 handleError();
2355 return;
2356 } else {
2357 handleExecute();
2358 if (DEBUG_OBB)
2359 Slog.i(TAG, "Posting install MCS_UNBIND");
2360 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
2361 }
2362 } catch (RemoteException e) {
2363 if (DEBUG_OBB)
2364 Slog.i(TAG, "Posting install MCS_RECONNECT");
2365 mObbActionHandler.sendEmptyMessage(OBB_MCS_RECONNECT);
2366 } catch (Exception e) {
2367 if (DEBUG_OBB)
2368 Slog.d(TAG, "Error handling OBB action", e);
2369 handleError();
Kenny Root17eb6fb2010-10-06 15:02:52 -07002370 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
Kenny Roota02b8b02010-08-05 16:14:17 -07002371 }
2372 }
2373
Kenny Root05105f72010-09-22 17:29:43 -07002374 abstract void handleExecute() throws RemoteException, IOException;
Kenny Roota02b8b02010-08-05 16:14:17 -07002375 abstract void handleError();
Kenny Root38cf8862010-09-26 14:18:51 -07002376
2377 protected ObbInfo getObbInfo() throws IOException {
2378 ObbInfo obbInfo;
2379 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002380 obbInfo = mContainerService.getObbInfo(mObbState.ownerPath);
Kenny Root38cf8862010-09-26 14:18:51 -07002381 } catch (RemoteException e) {
2382 Slog.d(TAG, "Couldn't call DefaultContainerService to fetch OBB info for "
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002383 + mObbState.ownerPath);
Kenny Root38cf8862010-09-26 14:18:51 -07002384 obbInfo = null;
2385 }
2386 if (obbInfo == null) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002387 throw new IOException("Couldn't read OBB file: " + mObbState.ownerPath);
Kenny Root38cf8862010-09-26 14:18:51 -07002388 }
2389 return obbInfo;
2390 }
2391
Kenny Rootaf9d6672010-10-08 09:21:39 -07002392 protected void sendNewStatusOrIgnore(int status) {
2393 if (mObbState == null || mObbState.token == null) {
2394 return;
2395 }
2396
Kenny Root38cf8862010-09-26 14:18:51 -07002397 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002398 mObbState.token.onObbResult(mObbState.rawPath, mObbState.nonce, status);
Kenny Root38cf8862010-09-26 14:18:51 -07002399 } catch (RemoteException e) {
2400 Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
2401 }
2402 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002403 }
2404
2405 class MountObbAction extends ObbAction {
Ben Komalo444eca22011-09-01 15:17:44 -07002406 private final String mKey;
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002407 private final int mCallingUid;
Kenny Roota02b8b02010-08-05 16:14:17 -07002408
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002409 MountObbAction(ObbState obbState, String key, int callingUid) {
Kenny Roota02b8b02010-08-05 16:14:17 -07002410 super(obbState);
2411 mKey = key;
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002412 mCallingUid = callingUid;
Kenny Roota02b8b02010-08-05 16:14:17 -07002413 }
2414
Jason parks5af0b912010-11-29 09:05:25 -06002415 @Override
Kenny Root735de3b2010-09-30 14:11:39 -07002416 public void handleExecute() throws IOException, RemoteException {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002417 waitForReady();
2418 warnOnNotMounted();
2419
Kenny Root38cf8862010-09-26 14:18:51 -07002420 final ObbInfo obbInfo = getObbInfo();
2421
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002422 if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mCallingUid)) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002423 Slog.w(TAG, "Denied attempt to mount OBB " + obbInfo.filename
2424 + " which is owned by " + obbInfo.packageName);
2425 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
2426 return;
Kenny Roota02b8b02010-08-05 16:14:17 -07002427 }
2428
Kenny Rootaf9d6672010-10-08 09:21:39 -07002429 final boolean isMounted;
2430 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002431 isMounted = mObbPathToStateMap.containsKey(mObbState.rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002432 }
2433 if (isMounted) {
2434 Slog.w(TAG, "Attempt to mount OBB which is already mounted: " + obbInfo.filename);
2435 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_ALREADY_MOUNTED);
2436 return;
2437 }
2438
Kenny Rootaf9d6672010-10-08 09:21:39 -07002439 final String hashedKey;
2440 if (mKey == null) {
2441 hashedKey = "none";
2442 } else {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002443 try {
Kenny Root3b1abba2010-10-13 15:00:07 -07002444 SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
2445
2446 KeySpec ks = new PBEKeySpec(mKey.toCharArray(), obbInfo.salt,
2447 PBKDF2_HASH_ROUNDS, CRYPTO_ALGORITHM_KEY_SIZE);
2448 SecretKey key = factory.generateSecret(ks);
2449 BigInteger bi = new BigInteger(key.getEncoded());
2450 hashedKey = bi.toString(16);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002451 } catch (NoSuchAlgorithmException e) {
Kenny Root3b1abba2010-10-13 15:00:07 -07002452 Slog.e(TAG, "Could not load PBKDF2 algorithm", e);
2453 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
2454 return;
2455 } catch (InvalidKeySpecException e) {
2456 Slog.e(TAG, "Invalid key spec when loading PBKDF2 algorithm", e);
2457 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
Kenny Root38cf8862010-09-26 14:18:51 -07002458 return;
2459 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07002460 }
Kenny Root38cf8862010-09-26 14:18:51 -07002461
Kenny Rootaf9d6672010-10-08 09:21:39 -07002462 int rc = StorageResultCode.OperationSucceeded;
Kenny Rootaf9d6672010-10-08 09:21:39 -07002463 try {
Jeff Sharkey56cd6462013-06-07 15:09:15 -07002464 mConnector.execute("obb", "mount", mObbState.voldPath, new SensitiveArg(hashedKey),
2465 mObbState.ownerGid);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002466 } catch (NativeDaemonConnectorException e) {
2467 int code = e.getCode();
2468 if (code != VoldResponseCode.OpFailedStorageBusy) {
2469 rc = StorageResultCode.OperationFailedInternalError;
Kenny Roota02b8b02010-08-05 16:14:17 -07002470 }
2471 }
2472
Kenny Rootaf9d6672010-10-08 09:21:39 -07002473 if (rc == StorageResultCode.OperationSucceeded) {
2474 if (DEBUG_OBB)
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002475 Slog.d(TAG, "Successfully mounted OBB " + mObbState.voldPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002476
2477 synchronized (mObbMounts) {
2478 addObbStateLocked(mObbState);
2479 }
2480
2481 sendNewStatusOrIgnore(OnObbStateChangeListener.MOUNTED);
Kenny Root02c87302010-07-01 08:10:18 -07002482 } else {
Kenny Root05105f72010-09-22 17:29:43 -07002483 Slog.e(TAG, "Couldn't mount OBB file: " + rc);
Kenny Roota02b8b02010-08-05 16:14:17 -07002484
Kenny Rootaf9d6672010-10-08 09:21:39 -07002485 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT);
Kenny Root02c87302010-07-01 08:10:18 -07002486 }
2487 }
2488
Jason parks5af0b912010-11-29 09:05:25 -06002489 @Override
Kenny Roota02b8b02010-08-05 16:14:17 -07002490 public void handleError() {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002491 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
Kenny Root02c87302010-07-01 08:10:18 -07002492 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002493
2494 @Override
2495 public String toString() {
2496 StringBuilder sb = new StringBuilder();
2497 sb.append("MountObbAction{");
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002498 sb.append(mObbState);
Kenny Roota02b8b02010-08-05 16:14:17 -07002499 sb.append('}');
2500 return sb.toString();
2501 }
2502 }
2503
2504 class UnmountObbAction extends ObbAction {
Ben Komalo444eca22011-09-01 15:17:44 -07002505 private final boolean mForceUnmount;
Kenny Roota02b8b02010-08-05 16:14:17 -07002506
2507 UnmountObbAction(ObbState obbState, boolean force) {
2508 super(obbState);
2509 mForceUnmount = force;
2510 }
2511
Jason parks5af0b912010-11-29 09:05:25 -06002512 @Override
Kenny Root38cf8862010-09-26 14:18:51 -07002513 public void handleExecute() throws IOException {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002514 waitForReady();
2515 warnOnNotMounted();
2516
Kenny Root38cf8862010-09-26 14:18:51 -07002517 final ObbInfo obbInfo = getObbInfo();
Kenny Roota02b8b02010-08-05 16:14:17 -07002518
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002519 final ObbState existingState;
Kenny Root38cf8862010-09-26 14:18:51 -07002520 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002521 existingState = mObbPathToStateMap.get(mObbState.rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002522 }
Kenny Root38cf8862010-09-26 14:18:51 -07002523
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002524 if (existingState == null) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002525 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_NOT_MOUNTED);
2526 return;
2527 }
2528
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002529 if (existingState.ownerGid != mObbState.ownerGid) {
2530 Slog.w(TAG, "Permission denied attempting to unmount OBB " + existingState.rawPath
2531 + " (owned by GID " + existingState.ownerGid + ")");
Kenny Rootaf9d6672010-10-08 09:21:39 -07002532 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
2533 return;
2534 }
2535
Kenny Rootaf9d6672010-10-08 09:21:39 -07002536 int rc = StorageResultCode.OperationSucceeded;
Kenny Rootaf9d6672010-10-08 09:21:39 -07002537 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002538 final Command cmd = new Command("obb", "unmount", mObbState.voldPath);
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002539 if (mForceUnmount) {
2540 cmd.appendArg("force");
2541 }
2542 mConnector.execute(cmd);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002543 } catch (NativeDaemonConnectorException e) {
2544 int code = e.getCode();
2545 if (code == VoldResponseCode.OpFailedStorageBusy) {
2546 rc = StorageResultCode.OperationFailedStorageBusy;
2547 } else if (code == VoldResponseCode.OpFailedStorageNotFound) {
2548 // If it's not mounted then we've already won.
2549 rc = StorageResultCode.OperationSucceeded;
2550 } else {
2551 rc = StorageResultCode.OperationFailedInternalError;
Kenny Roota02b8b02010-08-05 16:14:17 -07002552 }
2553 }
2554
Kenny Rootaf9d6672010-10-08 09:21:39 -07002555 if (rc == StorageResultCode.OperationSucceeded) {
2556 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002557 removeObbStateLocked(existingState);
Kenny Root38cf8862010-09-26 14:18:51 -07002558 }
2559
Kenny Rootaf9d6672010-10-08 09:21:39 -07002560 sendNewStatusOrIgnore(OnObbStateChangeListener.UNMOUNTED);
Kenny Roota02b8b02010-08-05 16:14:17 -07002561 } else {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002562 Slog.w(TAG, "Could not unmount OBB: " + existingState);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002563 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_UNMOUNT);
Kenny Roota02b8b02010-08-05 16:14:17 -07002564 }
2565 }
2566
Jason parks5af0b912010-11-29 09:05:25 -06002567 @Override
Kenny Roota02b8b02010-08-05 16:14:17 -07002568 public void handleError() {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002569 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
Kenny Roota02b8b02010-08-05 16:14:17 -07002570 }
2571
2572 @Override
2573 public String toString() {
2574 StringBuilder sb = new StringBuilder();
2575 sb.append("UnmountObbAction{");
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002576 sb.append(mObbState);
Kenny Roota02b8b02010-08-05 16:14:17 -07002577 sb.append(",force=");
2578 sb.append(mForceUnmount);
Kenny Roota02b8b02010-08-05 16:14:17 -07002579 sb.append('}');
2580 return sb.toString();
2581 }
Kenny Root02c87302010-07-01 08:10:18 -07002582 }
Kenny Root38cf8862010-09-26 14:18:51 -07002583
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -08002584 @VisibleForTesting
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002585 public static String buildObbPath(final String canonicalPath, int userId, boolean forVold) {
2586 // TODO: allow caller to provide Environment for full testing
Jeff Sharkey1abdb712013-08-11 16:28:14 -07002587 // TODO: extend to support OBB mounts on secondary external storage
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002588
2589 // Only adjust paths when storage is emulated
2590 if (!Environment.isExternalStorageEmulated()) {
2591 return canonicalPath;
2592 }
2593
2594 String path = canonicalPath.toString();
2595
2596 // First trim off any external storage prefix
2597 final UserEnvironment userEnv = new UserEnvironment(userId);
2598
2599 // /storage/emulated/0
Jeff Sharkey1abdb712013-08-11 16:28:14 -07002600 final String externalPath = userEnv.getExternalStorageDirectory().getAbsolutePath();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002601 // /storage/emulated_legacy
2602 final String legacyExternalPath = Environment.getLegacyExternalStorageDirectory()
Jeff Sharkey1abdb712013-08-11 16:28:14 -07002603 .getAbsolutePath();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002604
2605 if (path.startsWith(externalPath)) {
2606 path = path.substring(externalPath.length() + 1);
2607 } else if (path.startsWith(legacyExternalPath)) {
2608 path = path.substring(legacyExternalPath.length() + 1);
2609 } else {
2610 return canonicalPath;
2611 }
2612
2613 // Handle special OBB paths on emulated storage
2614 final String obbPath = "Android/obb";
2615 if (path.startsWith(obbPath)) {
2616 path = path.substring(obbPath.length() + 1);
2617
Jeff Sharkey48877892015-03-18 11:27:19 -07002618 final UserEnvironment ownerEnv = new UserEnvironment(UserHandle.USER_OWNER);
2619 return new File(ownerEnv.buildExternalStorageAndroidObbDirs()[0], path)
2620 .getAbsolutePath();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002621 }
2622
2623 // Handle normal external storage paths
Jeff Sharkey48877892015-03-18 11:27:19 -07002624 return new File(userEnv.getExternalStorageDirectory(), path).getAbsolutePath();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002625 }
2626
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07002627 private static class Callbacks extends Handler {
2628 private static final int MSG_STORAGE_STATE_CHANGED = 1;
2629 private static final int MSG_VOLUME_STATE_CHANGED = 2;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07002630 private static final int MSG_VOLUME_METADATA_CHANGED = 3;
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07002631
2632 private final RemoteCallbackList<IMountServiceListener>
2633 mCallbacks = new RemoteCallbackList<>();
2634
2635 public Callbacks(Looper looper) {
2636 super(looper);
2637 }
2638
2639 public void register(IMountServiceListener callback) {
2640 mCallbacks.register(callback);
2641 }
2642
2643 public void unregister(IMountServiceListener callback) {
2644 mCallbacks.unregister(callback);
2645 }
2646
2647 @Override
2648 public void handleMessage(Message msg) {
2649 final SomeArgs args = (SomeArgs) msg.obj;
2650 final int n = mCallbacks.beginBroadcast();
2651 for (int i = 0; i < n; i++) {
2652 final IMountServiceListener callback = mCallbacks.getBroadcastItem(i);
2653 try {
2654 invokeCallback(callback, msg.what, args);
2655 } catch (RemoteException ignored) {
2656 }
2657 }
2658 mCallbacks.finishBroadcast();
2659 args.recycle();
2660 }
2661
2662 private void invokeCallback(IMountServiceListener callback, int what, SomeArgs args)
2663 throws RemoteException {
2664 switch (what) {
2665 case MSG_STORAGE_STATE_CHANGED: {
2666 callback.onStorageStateChanged((String) args.arg1, (String) args.arg2,
2667 (String) args.arg3);
2668 break;
2669 }
2670 case MSG_VOLUME_STATE_CHANGED: {
2671 callback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3);
2672 break;
2673 }
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07002674 case MSG_VOLUME_METADATA_CHANGED: {
2675 callback.onVolumeMetadataChanged((VolumeInfo) args.arg1);
2676 break;
2677 }
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07002678 }
2679 }
2680
2681 private void notifyStorageStateChanged(String path, String oldState, String newState) {
2682 final SomeArgs args = SomeArgs.obtain();
2683 args.arg1 = path;
2684 args.arg2 = oldState;
2685 args.arg3 = newState;
2686 obtainMessage(MSG_STORAGE_STATE_CHANGED, args).sendToTarget();
2687 }
2688
2689 private void notifyVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
2690 final SomeArgs args = SomeArgs.obtain();
2691 args.arg1 = vol;
2692 args.argi2 = oldState;
2693 args.argi3 = newState;
2694 obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget();
2695 }
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07002696
2697 private void notifyVolumeMetadataChanged(VolumeInfo vol) {
2698 final SomeArgs args = SomeArgs.obtain();
2699 args.arg1 = vol;
2700 obtainMessage(MSG_VOLUME_METADATA_CHANGED, args).sendToTarget();
2701 }
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07002702 }
2703
Kenny Root38cf8862010-09-26 14:18:51 -07002704 @Override
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002705 protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
2706 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
2707
2708 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ", 160);
Kenny Root38cf8862010-09-26 14:18:51 -07002709
Kenny Root38cf8862010-09-26 14:18:51 -07002710 synchronized (mObbMounts) {
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002711 pw.println("mObbMounts:");
2712 pw.increaseIndent();
2713 final Iterator<Entry<IBinder, List<ObbState>>> binders = mObbMounts.entrySet()
2714 .iterator();
Kenny Rootaf9d6672010-10-08 09:21:39 -07002715 while (binders.hasNext()) {
2716 Entry<IBinder, List<ObbState>> e = binders.next();
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002717 pw.println(e.getKey() + ":");
2718 pw.increaseIndent();
Kenny Rootaf9d6672010-10-08 09:21:39 -07002719 final List<ObbState> obbStates = e.getValue();
Kenny Root38cf8862010-09-26 14:18:51 -07002720 for (final ObbState obbState : obbStates) {
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002721 pw.println(obbState);
Kenny Root38cf8862010-09-26 14:18:51 -07002722 }
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002723 pw.decreaseIndent();
Kenny Root38cf8862010-09-26 14:18:51 -07002724 }
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002725 pw.decreaseIndent();
Kenny Rootaf9d6672010-10-08 09:21:39 -07002726
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002727 pw.println();
2728 pw.println("mObbPathToStateMap:");
2729 pw.increaseIndent();
Kenny Rootaf9d6672010-10-08 09:21:39 -07002730 final Iterator<Entry<String, ObbState>> maps = mObbPathToStateMap.entrySet().iterator();
2731 while (maps.hasNext()) {
2732 final Entry<String, ObbState> e = maps.next();
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002733 pw.print(e.getKey());
2734 pw.print(" -> ");
2735 pw.println(e.getValue());
Kenny Rootaf9d6672010-10-08 09:21:39 -07002736 }
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002737 pw.decreaseIndent();
Kenny Root38cf8862010-09-26 14:18:51 -07002738 }
Kenny Root4161f9b2011-07-13 09:48:33 -07002739
Jeff Sharkey48877892015-03-18 11:27:19 -07002740 synchronized (mLock) {
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002741 pw.println();
Jeff Sharkey48877892015-03-18 11:27:19 -07002742 pw.println("Disks:");
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002743 pw.increaseIndent();
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002744 for (int i = 0; i < mDisks.size(); i++) {
2745 final DiskInfo disk = mDisks.valueAt(i);
Jeff Sharkey48877892015-03-18 11:27:19 -07002746 disk.dump(pw);
2747 }
2748 pw.decreaseIndent();
2749
2750 pw.println();
2751 pw.println("Volumes:");
2752 pw.increaseIndent();
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002753 for (int i = 0; i < mVolumes.size(); i++) {
2754 final VolumeInfo vol = mVolumes.valueAt(i);
Jeff Sharkey48877892015-03-18 11:27:19 -07002755 vol.dump(pw);
Kenny Root4161f9b2011-07-13 09:48:33 -07002756 }
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002757 pw.decreaseIndent();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07002758
2759 pw.println();
2760 pw.println("Metadata:");
2761 pw.increaseIndent();
2762 for (int i = 0; i < mMetadata.size(); i++) {
2763 final VolumeMetadata meta = mMetadata.valueAt(i);
2764 meta.dump(pw);
2765 }
2766 pw.decreaseIndent();
Kenny Root4161f9b2011-07-13 09:48:33 -07002767 }
Robert Greenwalt470fd722012-01-18 12:51:15 -08002768
2769 pw.println();
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002770 pw.println("mConnection:");
2771 pw.increaseIndent();
Robert Greenwalt470fd722012-01-18 12:51:15 -08002772 mConnector.dump(fd, pw, args);
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002773 pw.decreaseIndent();
Christopher Tate7265abe2014-11-21 13:54:45 -08002774
2775 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
2776
2777 pw.println();
2778 pw.print("Last maintenance: ");
2779 pw.println(sdf.format(new Date(mLastMaintenance)));
Kenny Root38cf8862010-09-26 14:18:51 -07002780 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002781
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -07002782 /** {@inheritDoc} */
Jeff Sharkey48877892015-03-18 11:27:19 -07002783 @Override
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -07002784 public void monitor() {
2785 if (mConnector != null) {
2786 mConnector.monitor();
2787 }
2788 }
2789}