blob: 0925fa52e524768cdd98ecb1d377028a6734d24c [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;
Jeff Sharkey275e3e42015-04-24 16:10:32 -070033import android.content.pm.IPackageMoveObserver;
34import android.content.pm.PackageManager;
Elliott Hughesf839b4f2014-09-26 12:30:47 -070035import android.content.res.Configuration;
Kenny Root02c87302010-07-01 08:10:18 -070036import android.content.res.ObbInfo;
Jeff Sharkey48877892015-03-18 11:27:19 -070037import android.mtp.MtpStorage;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038import android.net.Uri;
Kenny Root02c87302010-07-01 08:10:18 -070039import android.os.Binder;
Kenny Roota02b8b02010-08-05 16:14:17 -070040import android.os.Environment;
Jeff Sharkeyb049e212012-09-07 23:16:01 -070041import android.os.Environment.UserEnvironment;
Jeff Sharkey48877892015-03-18 11:27:19 -070042import android.os.FileUtils;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080043import android.os.Handler;
Dianne Hackbornefa92b22013-05-03 14:11:43 -070044import android.os.HandlerThread;
Kenny Roota02b8b02010-08-05 16:14:17 -070045import android.os.IBinder;
Daniel Sandler5f27ef42010-03-16 15:42:02 -040046import android.os.Looper;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080047import android.os.Message;
Jeff Sharkey7151a9a2015-04-04 15:22:37 -070048import android.os.RemoteCallbackList;
San Mehat4270e1e2010-01-29 05:32:19 -080049import android.os.RemoteException;
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -080050import android.os.ServiceManager;
Svetoslavf23b64d2013-04-25 14:45:54 -070051import android.os.SystemClock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052import android.os.SystemProperties;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070053import android.os.UserHandle;
Emily Bernier92aa5a22014-07-07 10:11:48 -040054import android.os.UserManager;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070055import android.os.storage.DiskInfo;
Kenny Roota02b8b02010-08-05 16:14:17 -070056import android.os.storage.IMountService;
57import android.os.storage.IMountServiceListener;
58import android.os.storage.IMountShutdownObserver;
59import android.os.storage.IObbActionListener;
Kenny Rootaf9d6672010-10-08 09:21:39 -070060import android.os.storage.OnObbStateChangeListener;
Paul Lawrence46791e72014-04-03 09:10:26 -070061import android.os.storage.StorageManager;
Kenny Roota02b8b02010-08-05 16:14:17 -070062import android.os.storage.StorageResultCode;
Mike Lockwood2f6a3882011-05-09 19:08:06 -070063import android.os.storage.StorageVolume;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070064import android.os.storage.VolumeInfo;
Jeff Sharkeyb36586a2015-04-27 08:42:28 -070065import android.os.storage.VolumeRecord;
Jason parksf7b3cd42011-01-27 09:28:25 -060066import android.text.TextUtils;
Jeff Sharkey1783f142015-04-17 10:52:51 -070067import android.text.format.DateUtils;
Jeff Sharkey48877892015-03-18 11:27:19 -070068import android.util.ArrayMap;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070069import android.util.AtomicFile;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070070import android.util.Log;
San Mehata5078592010-03-25 09:36:54 -070071import android.util.Slog;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070072import android.util.Xml;
Jeff Sharkey48877892015-03-18 11:27:19 -070073
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070074import libcore.io.IoUtils;
Jeff Sharkey48877892015-03-18 11:27:19 -070075import libcore.util.EmptyArray;
76import libcore.util.HexEncoding;
Mike Lockwood2f6a3882011-05-09 19:08:06 -070077
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -080078import com.android.internal.annotations.GuardedBy;
79import com.android.internal.annotations.VisibleForTesting;
Jeff Sharkeyb049e212012-09-07 23:16:01 -070080import com.android.internal.app.IMediaContainerService;
Jeff Sharkey7151a9a2015-04-04 15:22:37 -070081import com.android.internal.os.SomeArgs;
Jeff Sharkey48877892015-03-18 11:27:19 -070082import com.android.internal.util.ArrayUtils;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070083import com.android.internal.util.FastXmlSerializer;
Jeff Sharkey5aca2b82013-10-16 16:21:54 -070084import com.android.internal.util.IndentingPrintWriter;
Jeff Sharkey4fbbda42012-09-24 18:34:07 -070085import com.android.internal.util.Preconditions;
Jeff Sharkeyb049e212012-09-07 23:16:01 -070086import com.android.server.NativeDaemonConnector.Command;
Jeff Sharkey56cd6462013-06-07 15:09:15 -070087import com.android.server.NativeDaemonConnector.SensitiveArg;
Jeff Sharkeyb049e212012-09-07 23:16:01 -070088import com.android.server.pm.PackageManagerService;
Kenny Roota02b8b02010-08-05 16:14:17 -070089
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070090import org.xmlpull.v1.XmlPullParser;
91import org.xmlpull.v1.XmlPullParserException;
92import org.xmlpull.v1.XmlSerializer;
93
Jeff Sharkeyb049e212012-09-07 23:16:01 -070094import java.io.File;
Kenny Root38cf8862010-09-26 14:18:51 -070095import java.io.FileDescriptor;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070096import java.io.FileInputStream;
97import java.io.FileNotFoundException;
Christopher Tate7265abe2014-11-21 13:54:45 -080098import java.io.FileOutputStream;
Kenny Root05105f72010-09-22 17:29:43 -070099import java.io.IOException;
Kenny Root38cf8862010-09-26 14:18:51 -0700100import java.io.PrintWriter;
Kenny Root3b1abba2010-10-13 15:00:07 -0700101import java.math.BigInteger;
Paul Lawrence8e397362014-01-27 15:22:30 -0800102import java.nio.charset.StandardCharsets;
Kenny Root735de3b2010-09-30 14:11:39 -0700103import java.security.NoSuchAlgorithmException;
Kenny Root3b1abba2010-10-13 15:00:07 -0700104import java.security.spec.InvalidKeySpecException;
105import java.security.spec.KeySpec;
Christopher Tate7265abe2014-11-21 13:54:45 -0800106import java.text.SimpleDateFormat;
San Mehat22dd86e2010-01-12 12:21:18 -0800107import java.util.ArrayList;
Christopher Tate7265abe2014-11-21 13:54:45 -0800108import java.util.Date;
Kenny Roota02b8b02010-08-05 16:14:17 -0700109import java.util.HashMap;
San Mehat6cdd9c02010-02-09 14:45:20 -0800110import java.util.HashSet;
Kenny Root38cf8862010-09-26 14:18:51 -0700111import java.util.Iterator;
Kenny Roota02b8b02010-08-05 16:14:17 -0700112import java.util.LinkedList;
113import java.util.List;
Elliott Hughesf839b4f2014-09-26 12:30:47 -0700114import java.util.Locale;
Kenny Roota02b8b02010-08-05 16:14:17 -0700115import java.util.Map;
Kenny Root38cf8862010-09-26 14:18:51 -0700116import java.util.Map.Entry;
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700117import java.util.Objects;
Kenny Root51a573c2012-05-17 13:30:28 -0700118import java.util.concurrent.CountDownLatch;
119import java.util.concurrent.TimeUnit;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800120
Kenny Root3b1abba2010-10-13 15:00:07 -0700121import javax.crypto.SecretKey;
122import javax.crypto.SecretKeyFactory;
123import javax.crypto.spec.PBEKeySpec;
124
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125/**
Jeff Sharkey48877892015-03-18 11:27:19 -0700126 * Service responsible for various storage media. Connects to {@code vold} to
127 * watch for and manage dynamically added storage, such as SD cards and USB mass
128 * storage. Also decides how storage should be presented to users on the device.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800129 */
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -0700130class MountService extends IMountService.Stub
131 implements INativeDaemonConnectorCallbacks, Watchdog.Monitor {
Jason parks5af0b912010-11-29 09:05:25 -0600132
Jeff Sharkey48877892015-03-18 11:27:19 -0700133 // TODO: finish enforcing UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA
134
Christopher Tated417d622013-08-19 16:14:25 -0700135 // Static direct instance pointer for the tightly-coupled idle service to use
136 static MountService sSelf = null;
137
Jeff Sharkey56e62932015-03-21 20:41:00 -0700138 public static class Lifecycle extends SystemService {
139 private MountService mMountService;
140
141 public Lifecycle(Context context) {
142 super(context);
143 }
144
145 @Override
146 public void onStart() {
147 mMountService = new MountService(getContext());
148 publishBinderService("mount", mMountService);
149 }
150
151 @Override
152 public void onBootPhase(int phase) {
153 if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
154 mMountService.systemReady();
155 }
156 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700157
158 @Override
159 public void onStartUser(int userHandle) {
160 mMountService.onStartUser(userHandle);
161 }
162
163 @Override
164 public void onCleanupUser(int userHandle) {
165 mMountService.onCleanupUser(userHandle);
166 }
Jeff Sharkey56e62932015-03-21 20:41:00 -0700167 }
168
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800169 private static final boolean LOCAL_LOGD = false;
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800170 private static final boolean DEBUG_EVENTS = false;
Kenny Rootb7db2722011-01-25 16:39:35 -0800171 private static final boolean DEBUG_OBB = false;
Kenny Root02c87302010-07-01 08:10:18 -0700172
Kenny Root07714d42011-08-17 17:49:28 -0700173 // Disable this since it messes up long-running cryptfs operations.
174 private static final boolean WATCHDOG_ENABLE = false;
175
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800176 private static final String TAG = "MountService";
177
Kenny Root305bcbf2010-09-03 07:56:38 -0700178 private static final String VOLD_TAG = "VoldConnector";
179
Kenny Rootcf0b38c2011-03-22 14:17:59 -0700180 /** Maximum number of ASEC containers allowed to be mounted. */
181 private static final int MAX_CONTAINERS = 250;
182
Jeff Sharkey275e3e42015-04-24 16:10:32 -0700183 /** Magic value sent by MoveTask.cpp */
184 private static final int MOVE_STATUS_COPY_FINISHED = 82;
185
San Mehat4270e1e2010-01-29 05:32:19 -0800186 /*
187 * Internal vold response code constants
188 */
San Mehat22dd86e2010-01-12 12:21:18 -0800189 class VoldResponseCode {
San Mehat4270e1e2010-01-29 05:32:19 -0800190 /*
191 * 100 series - Requestion action was initiated; expect another reply
192 * before proceeding with a new command.
193 */
San Mehat22dd86e2010-01-12 12:21:18 -0800194 public static final int VolumeListResult = 110;
195 public static final int AsecListResult = 111;
San Mehatc1b4ce92010-02-16 17:13:03 -0800196 public static final int StorageUsersListResult = 112;
Paul Lawrencee51dcf92014-03-18 10:56:00 -0700197 public static final int CryptfsGetfieldResult = 113;
San Mehat22dd86e2010-01-12 12:21:18 -0800198
San Mehat4270e1e2010-01-29 05:32:19 -0800199 /*
200 * 200 series - Requestion action has been successfully completed.
201 */
202 public static final int ShareStatusResult = 210;
San Mehat22dd86e2010-01-12 12:21:18 -0800203 public static final int AsecPathResult = 211;
San Mehat4270e1e2010-01-29 05:32:19 -0800204 public static final int ShareEnabledResult = 212;
San Mehat22dd86e2010-01-12 12:21:18 -0800205
San Mehat4270e1e2010-01-29 05:32:19 -0800206 /*
207 * 400 series - Command was accepted, but the requested action
208 * did not take place.
209 */
210 public static final int OpFailedNoMedia = 401;
211 public static final int OpFailedMediaBlank = 402;
212 public static final int OpFailedMediaCorrupt = 403;
213 public static final int OpFailedVolNotMounted = 404;
San Mehatd9709982010-02-18 11:43:03 -0800214 public static final int OpFailedStorageBusy = 405;
San Mehat2d66cef2010-03-23 11:12:52 -0700215 public static final int OpFailedStorageNotFound = 406;
San Mehat4270e1e2010-01-29 05:32:19 -0800216
217 /*
218 * 600 series - Unsolicited broadcasts.
219 */
Jeff Sharkey48877892015-03-18 11:27:19 -0700220 public static final int DISK_CREATED = 640;
221 public static final int DISK_SIZE_CHANGED = 641;
222 public static final int DISK_LABEL_CHANGED = 642;
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700223 public static final int DISK_SCANNED = 643;
Jeff Sharkey48877892015-03-18 11:27:19 -0700224 public static final int DISK_DESTROYED = 649;
225
226 public static final int VOLUME_CREATED = 650;
227 public static final int VOLUME_STATE_CHANGED = 651;
228 public static final int VOLUME_FS_TYPE_CHANGED = 652;
229 public static final int VOLUME_FS_UUID_CHANGED = 653;
230 public static final int VOLUME_FS_LABEL_CHANGED = 654;
231 public static final int VOLUME_PATH_CHANGED = 655;
Jeff Sharkey50a05452015-04-29 11:24:52 -0700232 public static final int VOLUME_INTERNAL_PATH_CHANGED = 656;
Jeff Sharkey48877892015-03-18 11:27:19 -0700233 public static final int VOLUME_DESTROYED = 659;
Svetoslavf23b64d2013-04-25 14:45:54 -0700234
Jeff Sharkey275e3e42015-04-24 16:10:32 -0700235 public static final int MOVE_STATUS = 660;
236
Svetoslavf23b64d2013-04-25 14:45:54 -0700237 /*
238 * 700 series - fstrim
239 */
240 public static final int FstrimCompleted = 700;
San Mehat22dd86e2010-01-12 12:21:18 -0800241 }
242
Jeff Sharkey620b32b2015-04-23 19:36:02 -0700243 private static final int VERSION_INIT = 1;
244 private static final int VERSION_ADD_PRIMARY = 2;
245
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700246 private static final String TAG_VOLUMES = "volumes";
Jeff Sharkey620b32b2015-04-23 19:36:02 -0700247 private static final String ATTR_VERSION = "version";
248 private static final String ATTR_PRIMARY_STORAGE_UUID = "primaryStorageUuid";
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700249 private static final String TAG_VOLUME = "volume";
250 private static final String ATTR_TYPE = "type";
251 private static final String ATTR_FS_UUID = "fsUuid";
252 private static final String ATTR_NICKNAME = "nickname";
253 private static final String ATTR_USER_FLAGS = "userFlags";
254
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700255 private final AtomicFile mSettingsFile;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700256
Jeff Sharkey48877892015-03-18 11:27:19 -0700257 /**
258 * <em>Never</em> hold the lock while performing downcalls into vold, since
259 * unsolicited events can suddenly appear to update data structures.
260 */
261 private final Object mLock = new Object();
262
263 @GuardedBy("mLock")
264 private int[] mStartedUsers = EmptyArray.INT;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700265
266 /** Map from disk ID to disk */
Jeff Sharkey48877892015-03-18 11:27:19 -0700267 @GuardedBy("mLock")
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700268 private ArrayMap<String, DiskInfo> mDisks = new ArrayMap<>();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700269 /** Map from volume ID to disk */
Jeff Sharkey48877892015-03-18 11:27:19 -0700270 @GuardedBy("mLock")
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700271 private ArrayMap<String, VolumeInfo> mVolumes = new ArrayMap<>();
Jeff Sharkey48877892015-03-18 11:27:19 -0700272
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700273 /** Map from UUID to record */
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700274 @GuardedBy("mLock")
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700275 private ArrayMap<String, VolumeRecord> mRecords = new ArrayMap<>();
Jeff Sharkey620b32b2015-04-23 19:36:02 -0700276 @GuardedBy("mLock")
277 private String mPrimaryStorageUuid;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700278
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700279 /** Map from disk ID to latches */
280 @GuardedBy("mLock")
281 private ArrayMap<String, CountDownLatch> mDiskScanLatches = new ArrayMap<>();
282
Jeff Sharkey275e3e42015-04-24 16:10:32 -0700283 @GuardedBy("mLock")
284 private IPackageMoveObserver mMoveCallback;
285 @GuardedBy("mLock")
286 private String mMoveTargetUuid;
287
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700288 private DiskInfo findDiskById(String id) {
289 synchronized (mLock) {
290 final DiskInfo disk = mDisks.get(id);
291 if (disk != null) {
292 return disk;
293 }
294 }
295 throw new IllegalArgumentException("No disk found for ID " + id);
296 }
297
298 private VolumeInfo findVolumeById(String id) {
299 synchronized (mLock) {
300 final VolumeInfo vol = mVolumes.get(id);
301 if (vol != null) {
302 return vol;
303 }
304 }
305 throw new IllegalArgumentException("No volume found for ID " + id);
306 }
307
Jeff Sharkey48877892015-03-18 11:27:19 -0700308 @Deprecated
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700309 private String findVolumeIdForPath(String path) {
Jeff Sharkey48877892015-03-18 11:27:19 -0700310 synchronized (mLock) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700311 for (int i = 0; i < mVolumes.size(); i++) {
312 final VolumeInfo vol = mVolumes.valueAt(i);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700313 if (vol.path != null && path.startsWith(vol.path)) {
314 return vol.id;
Jeff Sharkey48877892015-03-18 11:27:19 -0700315 }
316 }
317 }
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700318 throw new IllegalArgumentException("No volume found for path " + path);
Jeff Sharkey48877892015-03-18 11:27:19 -0700319 }
320
Jeff Sharkey275e3e42015-04-24 16:10:32 -0700321 private VolumeInfo findStorageForUuid(String volumeUuid) {
322 final StorageManager storage = mContext.getSystemService(StorageManager.class);
323 if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) {
324 return findVolumeById(VolumeInfo.ID_EMULATED_INTERNAL);
325 } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
326 return storage.getPrimaryPhysicalVolume();
327 } else {
328 return storage.findEmulatedForPrivate(storage.findVolumeByUuid(volumeUuid));
329 }
330 }
331
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700332 private CountDownLatch findOrCreateDiskScanLatch(String diskId) {
333 synchronized (mLock) {
334 CountDownLatch latch = mDiskScanLatches.get(diskId);
335 if (latch == null) {
336 latch = new CountDownLatch(1);
337 mDiskScanLatches.put(diskId, latch);
338 }
339 return latch;
340 }
341 }
342
Jeff Sharkey48877892015-03-18 11:27:19 -0700343 private static int sNextMtpIndex = 1;
344
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700345 private static int allocateMtpIndex(String volId) {
346 if (VolumeInfo.ID_EMULATED_INTERNAL.equals(volId)) {
347 return 0;
348 } else {
349 return sNextMtpIndex++;
Jeff Sharkey48877892015-03-18 11:27:19 -0700350 }
351 }
352
Paul Lawrence8e397362014-01-27 15:22:30 -0800353 /** List of crypto types.
354 * These must match CRYPT_TYPE_XXX in cryptfs.h AND their
355 * corresponding commands in CommandListener.cpp */
356 public static final String[] CRYPTO_TYPES
357 = { "password", "default", "pattern", "pin" };
358
Brian Carlstrom7395a8a2014-04-28 22:11:01 -0700359 private final Context mContext;
Brian Carlstromdfad99a2014-05-07 15:21:14 -0700360 private final NativeDaemonConnector mConnector;
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700361
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700362 private volatile boolean mSystemReady = false;
Jeff Sharkey48877892015-03-18 11:27:19 -0700363 private volatile boolean mDaemonConnected = false;
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700364
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700365 private PackageManagerService mPms;
366
367 private final Callbacks mCallbacks;
Jeff Sharkey48877892015-03-18 11:27:19 -0700368
Jeff Sharkey0be607c2012-11-14 14:39:19 -0800369 private final CountDownLatch mConnectedSignal = new CountDownLatch(1);
370 private final CountDownLatch mAsecsScanned = new CountDownLatch(1);
Jeff Sharkey48877892015-03-18 11:27:19 -0700371
372 private final Object mUnmountLock = new Object();
373 @GuardedBy("mUnmountLock")
374 private CountDownLatch mUnmountSignal;
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -0800375
San Mehat6cdd9c02010-02-09 14:45:20 -0800376 /**
377 * Private hash of currently mounted secure containers.
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800378 * Used as a lock in methods to manipulate secure containers.
San Mehat6cdd9c02010-02-09 14:45:20 -0800379 */
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800380 final private HashSet<String> mAsecMountSet = new HashSet<String>();
San Mehat6cdd9c02010-02-09 14:45:20 -0800381
Kenny Root02c87302010-07-01 08:10:18 -0700382 /**
Kenny Root3b1abba2010-10-13 15:00:07 -0700383 * The size of the crypto algorithm key in bits for OBB files. Currently
384 * Twofish is used which takes 128-bit keys.
385 */
386 private static final int CRYPTO_ALGORITHM_KEY_SIZE = 128;
387
388 /**
389 * The number of times to run SHA1 in the PBKDF2 function for OBB files.
390 * 1024 is reasonably secure and not too slow.
391 */
392 private static final int PBKDF2_HASH_ROUNDS = 1024;
393
394 /**
Kenny Roota02b8b02010-08-05 16:14:17 -0700395 * Mounted OBB tracking information. Used to track the current state of all
396 * OBBs.
Kenny Root02c87302010-07-01 08:10:18 -0700397 */
Kenny Root735de3b2010-09-30 14:11:39 -0700398 final private Map<IBinder, List<ObbState>> mObbMounts = new HashMap<IBinder, List<ObbState>>();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700399
400 /** Map from raw paths to {@link ObbState}. */
Kenny Roota02b8b02010-08-05 16:14:17 -0700401 final private Map<String, ObbState> mObbPathToStateMap = new HashMap<String, ObbState>();
402
403 class ObbState implements IBinder.DeathRecipient {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700404 public ObbState(String rawPath, String canonicalPath, int callingUid,
405 IObbActionListener token, int nonce) {
406 this.rawPath = rawPath;
407 this.canonicalPath = canonicalPath.toString();
408
409 final int userId = UserHandle.getUserId(callingUid);
410 this.ownerPath = buildObbPath(canonicalPath, userId, false);
411 this.voldPath = buildObbPath(canonicalPath, userId, true);
412
413 this.ownerGid = UserHandle.getSharedAppGid(callingUid);
Kenny Rootaf9d6672010-10-08 09:21:39 -0700414 this.token = token;
415 this.nonce = nonce;
Kenny Roota02b8b02010-08-05 16:14:17 -0700416 }
417
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700418 final String rawPath;
419 final String canonicalPath;
420 final String ownerPath;
421 final String voldPath;
Kenny Roota02b8b02010-08-05 16:14:17 -0700422
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700423 final int ownerGid;
Kenny Roota02b8b02010-08-05 16:14:17 -0700424
Kenny Rootaf9d6672010-10-08 09:21:39 -0700425 // Token of remote Binder caller
426 final IObbActionListener token;
427
428 // Identifier to pass back to the token
429 final int nonce;
Kenny Roota02b8b02010-08-05 16:14:17 -0700430
Kenny Root735de3b2010-09-30 14:11:39 -0700431 public IBinder getBinder() {
432 return token.asBinder();
433 }
434
Kenny Roota02b8b02010-08-05 16:14:17 -0700435 @Override
436 public void binderDied() {
437 ObbAction action = new UnmountObbAction(this, true);
438 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
Kenny Root735de3b2010-09-30 14:11:39 -0700439 }
Kenny Roota02b8b02010-08-05 16:14:17 -0700440
Kenny Root5919ac62010-10-05 09:49:40 -0700441 public void link() throws RemoteException {
442 getBinder().linkToDeath(this, 0);
443 }
444
445 public void unlink() {
Kenny Root735de3b2010-09-30 14:11:39 -0700446 getBinder().unlinkToDeath(this, 0);
Kenny Roota02b8b02010-08-05 16:14:17 -0700447 }
Kenny Root38cf8862010-09-26 14:18:51 -0700448
449 @Override
450 public String toString() {
451 StringBuilder sb = new StringBuilder("ObbState{");
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700452 sb.append("rawPath=").append(rawPath);
453 sb.append(",canonicalPath=").append(canonicalPath);
454 sb.append(",ownerPath=").append(ownerPath);
455 sb.append(",voldPath=").append(voldPath);
456 sb.append(",ownerGid=").append(ownerGid);
457 sb.append(",token=").append(token);
458 sb.append(",binder=").append(getBinder());
Kenny Root38cf8862010-09-26 14:18:51 -0700459 sb.append('}');
460 return sb.toString();
461 }
Kenny Roota02b8b02010-08-05 16:14:17 -0700462 }
463
464 // OBB Action Handler
465 final private ObbActionHandler mObbActionHandler;
466
467 // OBB action handler messages
468 private static final int OBB_RUN_ACTION = 1;
469 private static final int OBB_MCS_BOUND = 2;
470 private static final int OBB_MCS_UNBIND = 3;
471 private static final int OBB_MCS_RECONNECT = 4;
Kenny Rootaf9d6672010-10-08 09:21:39 -0700472 private static final int OBB_FLUSH_MOUNT_STATE = 5;
Kenny Roota02b8b02010-08-05 16:14:17 -0700473
474 /*
475 * Default Container Service information
476 */
477 static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
478 "com.android.defcontainer", "com.android.defcontainer.DefaultContainerService");
479
480 final private DefaultContainerConnection mDefContainerConn = new DefaultContainerConnection();
481
482 class DefaultContainerConnection implements ServiceConnection {
Jeff Sharkey48877892015-03-18 11:27:19 -0700483 @Override
Kenny Roota02b8b02010-08-05 16:14:17 -0700484 public void onServiceConnected(ComponentName name, IBinder service) {
485 if (DEBUG_OBB)
486 Slog.i(TAG, "onServiceConnected");
487 IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(service);
488 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_MCS_BOUND, imcs));
489 }
490
Jeff Sharkey48877892015-03-18 11:27:19 -0700491 @Override
Kenny Roota02b8b02010-08-05 16:14:17 -0700492 public void onServiceDisconnected(ComponentName name) {
493 if (DEBUG_OBB)
494 Slog.i(TAG, "onServiceDisconnected");
495 }
496 };
497
498 // Used in the ObbActionHandler
499 private IMediaContainerService mContainerService = null;
Kenny Root02c87302010-07-01 08:10:18 -0700500
Christopher Tate7265abe2014-11-21 13:54:45 -0800501 // Last fstrim operation tracking
502 private static final String LAST_FSTRIM_FILE = "last-fstrim";
503 private final File mLastMaintenanceFile;
504 private long mLastMaintenance;
505
Kenny Root02c87302010-07-01 08:10:18 -0700506 // Handler messages
Jeff Sharkey48877892015-03-18 11:27:19 -0700507 private static final int H_SYSTEM_READY = 1;
508 private static final int H_DAEMON_CONNECTED = 2;
509 private static final int H_SHUTDOWN = 3;
510 private static final int H_FSTRIM = 4;
511 private static final int H_VOLUME_MOUNT = 5;
512 private static final int H_VOLUME_BROADCAST = 6;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800513
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400514 class MountServiceHandler extends Handler {
Jeff Sharkey48877892015-03-18 11:27:19 -0700515 public MountServiceHandler(Looper looper) {
516 super(looper);
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400517 }
518
Jason parks5af0b912010-11-29 09:05:25 -0600519 @Override
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800520 public void handleMessage(Message msg) {
521 switch (msg.what) {
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700522 case H_SYSTEM_READY: {
Jeff Sharkey48877892015-03-18 11:27:19 -0700523 handleSystemReady();
524 break;
525 }
526 case H_DAEMON_CONNECTED: {
527 handleDaemonConnected();
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700528 break;
529 }
Christopher Tated417d622013-08-19 16:14:25 -0700530 case H_FSTRIM: {
Jeff Sharkey1783f142015-04-17 10:52:51 -0700531 if (!isReady()) {
532 Slog.i(TAG, "fstrim requested, but no daemon connection yet; trying again");
Christopher Tate7618db12015-04-28 16:32:55 -0700533 sendMessageDelayed(obtainMessage(H_FSTRIM, msg.obj),
534 DateUtils.SECOND_IN_MILLIS);
535 break;
Jeff Sharkey1783f142015-04-17 10:52:51 -0700536 }
537
Christopher Tated417d622013-08-19 16:14:25 -0700538 Slog.i(TAG, "Running fstrim idle maintenance");
Christopher Tate7265abe2014-11-21 13:54:45 -0800539
540 // Remember when we kicked it off
541 try {
542 mLastMaintenance = System.currentTimeMillis();
543 mLastMaintenanceFile.setLastModified(mLastMaintenance);
544 } catch (Exception e) {
545 Slog.e(TAG, "Unable to record last fstrim!");
546 }
547
Christopher Tated417d622013-08-19 16:14:25 -0700548 try {
549 // This method must be run on the main (handler) thread,
550 // so it is safe to directly call into vold.
551 mConnector.execute("fstrim", "dotrim");
552 EventLogTags.writeFstrimStart(SystemClock.elapsedRealtime());
553 } catch (NativeDaemonConnectorException ndce) {
554 Slog.e(TAG, "Failed to run fstrim!");
555 }
Christopher Tate7265abe2014-11-21 13:54:45 -0800556
Christopher Tated417d622013-08-19 16:14:25 -0700557 // invoke the completion callback, if any
558 Runnable callback = (Runnable) msg.obj;
559 if (callback != null) {
560 callback.run();
561 }
562 break;
563 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700564 case H_SHUTDOWN: {
565 final IMountShutdownObserver obs = (IMountShutdownObserver) msg.obj;
566 boolean success = false;
567 try {
568 success = mConnector.execute("volume", "shutdown").isClassOk();
569 } catch (NativeDaemonConnectorException ignored) {
570 }
571 if (obs != null) {
572 try {
573 obs.onShutDownComplete(success ? 0 : -1);
574 } catch (RemoteException ignored) {
575 }
576 }
577 break;
578 }
579 case H_VOLUME_MOUNT: {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700580 final VolumeInfo vol = (VolumeInfo) msg.obj;
Jeff Sharkey48877892015-03-18 11:27:19 -0700581 try {
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700582 mConnector.execute("volume", "mount", vol.id, vol.mountFlags,
583 vol.mountUserId);
Jeff Sharkey48877892015-03-18 11:27:19 -0700584 } catch (NativeDaemonConnectorException ignored) {
585 }
586 break;
587 }
588 case H_VOLUME_BROADCAST: {
589 final StorageVolume userVol = (StorageVolume) msg.obj;
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700590 final String envState = userVol.getState();
591 Slog.d(TAG, "Volume " + userVol.getId() + " broadcasting " + envState + " to "
Jeff Sharkey48877892015-03-18 11:27:19 -0700592 + userVol.getOwner());
593
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700594 final String action = VolumeInfo.getBroadcastForEnvironment(envState);
Jeff Sharkey48877892015-03-18 11:27:19 -0700595 if (action != null) {
596 final Intent intent = new Intent(action,
597 Uri.fromFile(userVol.getPathFile()));
598 intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, userVol);
599 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
600 mContext.sendBroadcastAsUser(intent, userVol.getOwner());
601 }
602 break;
603 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800604 }
605 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700606 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700607
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700608 private final Handler mHandler;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800609
Jeff Sharkey56e62932015-03-21 20:41:00 -0700610 @Override
611 public void waitForAsecScan() {
Jeff Sharkey48877892015-03-18 11:27:19 -0700612 waitForLatch(mAsecsScanned, "mAsecsScanned");
Kenny Root51a573c2012-05-17 13:30:28 -0700613 }
614
San Mehat207e5382010-02-04 20:46:54 -0800615 private void waitForReady() {
Jeff Sharkey48877892015-03-18 11:27:19 -0700616 waitForLatch(mConnectedSignal, "mConnectedSignal");
Kenny Root51a573c2012-05-17 13:30:28 -0700617 }
618
Jeff Sharkey48877892015-03-18 11:27:19 -0700619 private void waitForLatch(CountDownLatch latch, String condition) {
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700620 while (true) {
Kenny Root51a573c2012-05-17 13:30:28 -0700621 try {
622 if (latch.await(5000, TimeUnit.MILLISECONDS)) {
San Mehat207e5382010-02-04 20:46:54 -0800623 return;
Kenny Root51a573c2012-05-17 13:30:28 -0700624 } else {
625 Slog.w(TAG, "Thread " + Thread.currentThread().getName()
Jeff Sharkey48877892015-03-18 11:27:19 -0700626 + " still waiting for " + condition + "...");
San Mehat207e5382010-02-04 20:46:54 -0800627 }
Kenny Root51a573c2012-05-17 13:30:28 -0700628 } catch (InterruptedException e) {
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700629 Slog.w(TAG, "Interrupt while waiting for " + condition);
San Mehat207e5382010-02-04 20:46:54 -0800630 }
San Mehat207e5382010-02-04 20:46:54 -0800631 }
San Mehat1f6301e2010-01-07 22:40:27 -0800632 }
Kenny Root02c87302010-07-01 08:10:18 -0700633
Paul Lawrence945490c2014-03-27 16:37:28 +0000634 private boolean isReady() {
635 try {
636 return mConnectedSignal.await(0, TimeUnit.MILLISECONDS);
637 } catch (InterruptedException e) {
638 return false;
639 }
640 }
641
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700642 private void handleSystemReady() {
Jeff Sharkey48877892015-03-18 11:27:19 -0700643 resetIfReadyAndConnected();
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700644
Jeff Sharkey48877892015-03-18 11:27:19 -0700645 // Start scheduling nominally-daily fstrim operations
Christopher Tate115afda2014-06-06 19:06:26 -0700646 MountServiceIdler.scheduleIdlePass(mContext);
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700647 }
648
Jeff Sharkey48877892015-03-18 11:27:19 -0700649 private void resetIfReadyAndConnected() {
650 Slog.d(TAG, "Thinking about reset, mSystemReady=" + mSystemReady
651 + ", mDaemonConnected=" + mDaemonConnected);
652 if (mSystemReady && mDaemonConnected) {
653 mDisks.clear();
654 mVolumes.clear();
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700655
Jeff Sharkey59d577a2015-04-11 21:27:21 -0700656 // Create a stub volume that represents internal storage
657 final VolumeInfo internal = new VolumeInfo(VolumeInfo.ID_PRIVATE_INTERNAL,
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700658 VolumeInfo.TYPE_PRIVATE, null, 0);
Jeff Sharkey59d577a2015-04-11 21:27:21 -0700659 internal.state = VolumeInfo.STATE_MOUNTED;
660 internal.path = Environment.getDataDirectory().getAbsolutePath();
661 mVolumes.put(internal.id, internal);
662
Jeff Sharkey48877892015-03-18 11:27:19 -0700663 try {
664 mConnector.execute("volume", "reset");
Jeff Sharkey50a05452015-04-29 11:24:52 -0700665 for (int userId : mStartedUsers) {
666 mConnector.execute("volume", "start_user", userId);
667 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700668 } catch (NativeDaemonConnectorException e) {
669 Slog.w(TAG, "Failed to reset vold", e);
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700670 }
671 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700672 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700673
Jeff Sharkey48877892015-03-18 11:27:19 -0700674 private void onStartUser(int userId) {
675 Slog.d(TAG, "onStartUser " + userId);
676
677 // We purposefully block here to make sure that user-specific
678 // staging area is ready so it's ready for zygote-forked apps to
679 // bind mount against.
680 try {
681 mConnector.execute("volume", "start_user", userId);
682 } catch (NativeDaemonConnectorException ignored) {
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700683 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700684
685 // Record user as started so newly mounted volumes kick off events
686 // correctly, then synthesize events for any already-mounted volumes.
687 synchronized (mVolumes) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700688 for (int i = 0; i < mVolumes.size(); i++) {
689 final VolumeInfo vol = mVolumes.valueAt(i);
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700690 if (vol.isVisibleToUser(userId) && vol.isMountedReadable()) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700691 final StorageVolume userVol = vol.buildStorageVolume(mContext, userId);
Jeff Sharkey48877892015-03-18 11:27:19 -0700692 mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget();
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700693
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700694 final String envState = VolumeInfo.getEnvironmentForState(vol.getState());
695 mCallbacks.notifyStorageStateChanged(userVol.getPath(), envState, envState);
Jeff Sharkey48877892015-03-18 11:27:19 -0700696 }
697 }
698 mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userId);
699 }
700 }
701
702 private void onCleanupUser(int userId) {
703 Slog.d(TAG, "onCleanupUser " + userId);
704
705 try {
706 mConnector.execute("volume", "cleanup_user", userId);
707 } catch (NativeDaemonConnectorException ignored) {
708 }
709
710 synchronized (mVolumes) {
711 mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userId);
712 }
713 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700714
Christopher Tated417d622013-08-19 16:14:25 -0700715 void runIdleMaintenance(Runnable callback) {
716 mHandler.sendMessage(mHandler.obtainMessage(H_FSTRIM, callback));
717 }
718
Christopher Tate7265abe2014-11-21 13:54:45 -0800719 // Binder entry point for kicking off an immediate fstrim
720 @Override
721 public void runMaintenance() {
Jeff Sharkey48877892015-03-18 11:27:19 -0700722 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
Christopher Tate7265abe2014-11-21 13:54:45 -0800723 runIdleMaintenance(null);
724 }
725
726 @Override
727 public long lastMaintenance() {
728 return mLastMaintenance;
729 }
730
San Mehat4270e1e2010-01-29 05:32:19 -0800731 /**
San Mehat4270e1e2010-01-29 05:32:19 -0800732 * Callback from NativeDaemonConnector
733 */
Jeff Sharkey48877892015-03-18 11:27:19 -0700734 @Override
San Mehat4270e1e2010-01-29 05:32:19 -0800735 public void onDaemonConnected() {
Jeff Sharkey48877892015-03-18 11:27:19 -0700736 mDaemonConnected = true;
737 mHandler.obtainMessage(H_DAEMON_CONNECTED).sendToTarget();
738 }
739
740 private void handleDaemonConnected() {
741 resetIfReadyAndConnected();
742
San Mehat4270e1e2010-01-29 05:32:19 -0800743 /*
Jeff Sharkey48877892015-03-18 11:27:19 -0700744 * Now that we've done our initialization, release
745 * the hounds!
San Mehat4270e1e2010-01-29 05:32:19 -0800746 */
Jeff Sharkey48877892015-03-18 11:27:19 -0700747 mConnectedSignal.countDown();
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400748
Jeff Sharkey48877892015-03-18 11:27:19 -0700749 // On an encrypted device we can't see system properties yet, so pull
750 // the system locale out of the mount service.
751 if ("".equals(SystemProperties.get("vold.encrypt_progress"))) {
752 copyLocaleFromMountService();
753 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700754
Jeff Sharkey48877892015-03-18 11:27:19 -0700755 // Let package manager load internal ASECs.
756 mPms.scanAvailableAsecs();
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400757
Jeff Sharkey48877892015-03-18 11:27:19 -0700758 // Notify people waiting for ASECs to be scanned that it's done.
759 mAsecsScanned.countDown();
San Mehat4270e1e2010-01-29 05:32:19 -0800760 }
761
Elliott Hughesf839b4f2014-09-26 12:30:47 -0700762 private void copyLocaleFromMountService() {
763 String systemLocale;
764 try {
765 systemLocale = getField(StorageManager.SYSTEM_LOCALE_KEY);
766 } catch (RemoteException e) {
767 return;
768 }
769 if (TextUtils.isEmpty(systemLocale)) {
770 return;
771 }
772
773 Slog.d(TAG, "Got locale " + systemLocale + " from mount service");
774 Locale locale = Locale.forLanguageTag(systemLocale);
775 Configuration config = new Configuration();
776 config.setLocale(locale);
777 try {
778 ActivityManagerNative.getDefault().updateConfiguration(config);
779 } catch (RemoteException e) {
780 Slog.e(TAG, "Error setting system locale from mount service", e);
781 }
Elliott Hughes9c33f282014-10-13 12:39:56 -0700782
783 // Temporary workaround for http://b/17945169.
784 Slog.d(TAG, "Setting system properties to " + systemLocale + " from mount service");
Narayan Kamathd30dbb82015-01-15 14:48:15 +0000785 SystemProperties.set("persist.sys.locale", locale.toLanguageTag());
Elliott Hughesf839b4f2014-09-26 12:30:47 -0700786 }
787
San Mehat4270e1e2010-01-29 05:32:19 -0800788 /**
San Mehat4270e1e2010-01-29 05:32:19 -0800789 * Callback from NativeDaemonConnector
790 */
Jeff Sharkey48877892015-03-18 11:27:19 -0700791 @Override
Dianne Hackborn77b987f2014-02-26 16:20:52 -0800792 public boolean onCheckHoldWakeLock(int code) {
793 return false;
794 }
795
796 /**
797 * Callback from NativeDaemonConnector
798 */
Jeff Sharkey48877892015-03-18 11:27:19 -0700799 @Override
San Mehat4270e1e2010-01-29 05:32:19 -0800800 public boolean onEvent(int code, String raw, String[] cooked) {
Jeff Sharkey48877892015-03-18 11:27:19 -0700801 synchronized (mLock) {
802 return onEventLocked(code, raw, cooked);
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800803 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700804 }
Jeff Sharkey5aca2b82013-10-16 16:21:54 -0700805
Jeff Sharkey48877892015-03-18 11:27:19 -0700806 private boolean onEventLocked(int code, String raw, String[] cooked) {
807 switch (code) {
808 case VoldResponseCode.DISK_CREATED: {
809 if (cooked.length != 3) break;
810 final String id = cooked[1];
Jeff Sharkey74acbbb2015-04-21 12:14:03 -0700811 int flags = Integer.parseInt(cooked[2]);
812 if (SystemProperties.getBoolean(StorageManager.PROP_FORCE_ADOPTABLE, false)) {
813 flags |= DiskInfo.FLAG_ADOPTABLE;
814 }
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700815 mDisks.put(id, new DiskInfo(id, flags));
Jeff Sharkey48877892015-03-18 11:27:19 -0700816 break;
Jeff Sharkey5aca2b82013-10-16 16:21:54 -0700817 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700818 case VoldResponseCode.DISK_SIZE_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.size = Long.parseLong(cooked[2]);
San Mehat4270e1e2010-01-29 05:32:19 -0800823 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700824 break;
825 }
826 case VoldResponseCode.DISK_LABEL_CHANGED: {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700827 final DiskInfo disk = mDisks.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -0700828 if (disk != null) {
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700829 final StringBuilder builder = new StringBuilder();
830 for (int i = 2; i < cooked.length; i++) {
831 builder.append(cooked[i]).append(' ');
832 }
833 disk.label = builder.toString().trim();
Jeff Sharkey48877892015-03-18 11:27:19 -0700834 }
835 break;
836 }
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700837 case VoldResponseCode.DISK_SCANNED: {
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700838 if (cooked.length != 2) break;
839 final DiskInfo disk = mDisks.get(cooked[1]);
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700840 if (disk != null) {
841 onDiskScannedLocked(disk);
842 }
Jeff Sharkey59d577a2015-04-11 21:27:21 -0700843 break;
844 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700845 case VoldResponseCode.DISK_DESTROYED: {
846 if (cooked.length != 2) break;
847 mDisks.remove(cooked[1]);
848 break;
849 }
San Mehat4270e1e2010-01-29 05:32:19 -0800850
Jeff Sharkey48877892015-03-18 11:27:19 -0700851 case VoldResponseCode.VOLUME_CREATED: {
Jeff Sharkey48877892015-03-18 11:27:19 -0700852 final String id = cooked[1];
853 final int type = Integer.parseInt(cooked[2]);
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700854 final String diskId = (cooked.length == 4) ? cooked[3] : null;
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700855 final DiskInfo disk = mDisks.get(diskId);
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700856 final int mtpIndex = allocateMtpIndex(id);
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700857 final VolumeInfo vol = new VolumeInfo(id, type, disk, mtpIndex);
Jeff Sharkey48877892015-03-18 11:27:19 -0700858 mVolumes.put(id, vol);
859 onVolumeCreatedLocked(vol);
860 break;
861 }
862 case VoldResponseCode.VOLUME_STATE_CHANGED: {
863 if (cooked.length != 3) break;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700864 final VolumeInfo vol = mVolumes.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -0700865 if (vol != null) {
866 final int oldState = vol.state;
867 final int newState = Integer.parseInt(cooked[2]);
868 vol.state = newState;
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700869 onVolumeStateChangedLocked(vol, oldState, newState);
Jeff Sharkey48877892015-03-18 11:27:19 -0700870 }
871 break;
872 }
873 case VoldResponseCode.VOLUME_FS_TYPE_CHANGED: {
874 if (cooked.length != 3) break;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700875 final VolumeInfo vol = mVolumes.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -0700876 if (vol != null) {
877 vol.fsType = cooked[2];
878 }
879 break;
880 }
881 case VoldResponseCode.VOLUME_FS_UUID_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.fsUuid = cooked[2];
886 }
887 break;
888 }
889 case VoldResponseCode.VOLUME_FS_LABEL_CHANGED: {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700890 final VolumeInfo vol = mVolumes.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -0700891 if (vol != null) {
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700892 final StringBuilder builder = new StringBuilder();
893 for (int i = 2; i < cooked.length; i++) {
894 builder.append(cooked[i]).append(' ');
895 }
896 vol.fsLabel = builder.toString().trim();
Jeff Sharkey48877892015-03-18 11:27:19 -0700897 }
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700898 // TODO: notify listeners that label changed
Jeff Sharkey48877892015-03-18 11:27:19 -0700899 break;
900 }
901 case VoldResponseCode.VOLUME_PATH_CHANGED: {
902 if (cooked.length != 3) break;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700903 final VolumeInfo vol = mVolumes.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -0700904 if (vol != null) {
905 vol.path = cooked[2];
906 }
907 break;
908 }
Jeff Sharkey50a05452015-04-29 11:24:52 -0700909 case VoldResponseCode.VOLUME_INTERNAL_PATH_CHANGED: {
910 if (cooked.length != 3) break;
911 final VolumeInfo vol = mVolumes.get(cooked[1]);
912 if (vol != null) {
913 vol.internalPath = cooked[2];
914 }
915 break;
916 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700917 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 Sharkey275e3e42015-04-24 16:10:32 -0700923 case VoldResponseCode.MOVE_STATUS: {
924 final int status = Integer.parseInt(cooked[1]);
925 onMoveStatusLocked(status);
926 break;
927 }
928
Jeff Sharkey48877892015-03-18 11:27:19 -0700929 case VoldResponseCode.FstrimCompleted: {
Svetoslav9e814a82013-04-30 10:43:56 -0700930 EventLogTags.writeFstrimFinish(SystemClock.elapsedRealtime());
Jeff Sharkey48877892015-03-18 11:27:19 -0700931 break;
San Mehat4270e1e2010-01-29 05:32:19 -0800932 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700933 default: {
934 Slog.d(TAG, "Unhandled vold event " + code);
Mike Lockwooda5250c92011-05-23 13:44:04 -0400935 }
San Mehat4270e1e2010-01-29 05:32:19 -0800936 }
937
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400938 return true;
San Mehat4270e1e2010-01-29 05:32:19 -0800939 }
940
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700941 private void onDiskScannedLocked(DiskInfo disk) {
Jeff Sharkey620b32b2015-04-23 19:36:02 -0700942 final Intent intent = new Intent(DiskInfo.ACTION_DISK_SCANNED);
943 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
944 mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
945 android.Manifest.permission.WRITE_MEDIA_STORAGE);
946
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700947 final CountDownLatch latch = mDiskScanLatches.remove(disk.id);
948 if (latch != null) {
949 latch.countDown();
950 }
951
Jeff Sharkey620b32b2015-04-23 19:36:02 -0700952 int volumeCount = 0;
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700953 for (int i = 0; i < mVolumes.size(); i++) {
954 final VolumeInfo vol = mVolumes.valueAt(i);
955 if (Objects.equals(disk.id, vol.getDiskId())) {
Jeff Sharkey620b32b2015-04-23 19:36:02 -0700956 volumeCount++;
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700957 }
958 }
959
Jeff Sharkey620b32b2015-04-23 19:36:02 -0700960 mCallbacks.notifyDiskScanned(disk, volumeCount);
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700961 }
962
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700963 private void onVolumeCreatedLocked(VolumeInfo vol) {
Jeff Sharkey275e3e42015-04-24 16:10:32 -0700964 if (vol.type == VolumeInfo.TYPE_EMULATED) {
965 final StorageManager storage = mContext.getSystemService(StorageManager.class);
966 final VolumeInfo privateVol = storage.findPrivateForEmulated(vol);
967
968 if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, mPrimaryStorageUuid)
969 && VolumeInfo.ID_PRIVATE_INTERNAL.equals(privateVol.id)) {
970 Slog.v(TAG, "Found primary storage at " + vol);
971 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
972 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
973 mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
974
975 } else if (Objects.equals(privateVol.fsUuid, mPrimaryStorageUuid)) {
976 Slog.v(TAG, "Found primary storage at " + vol);
977 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
978 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
979 mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
980 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700981
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700982 } else if (vol.type == VolumeInfo.TYPE_PUBLIC) {
Jeff Sharkey275e3e42015-04-24 16:10:32 -0700983 // TODO: only look at first public partition
984 if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, mPrimaryStorageUuid)
985 && vol.disk.isDefaultPrimary()) {
986 Slog.v(TAG, "Found primary storage at " + vol);
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700987 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
988 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
San Mehat4270e1e2010-01-29 05:32:19 -0800989 }
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700990
991 // Adoptable public disks are visible to apps, since they meet
992 // public API requirement of being in a stable location.
Jeff Sharkey275e3e42015-04-24 16:10:32 -0700993 if (vol.disk.isAdoptable()) {
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700994 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
995 }
996
997 vol.mountUserId = UserHandle.USER_OWNER;
Jeff Sharkey48877892015-03-18 11:27:19 -0700998 mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
San Mehat4270e1e2010-01-29 05:32:19 -0800999
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -07001000 } else if (vol.type == VolumeInfo.TYPE_PRIVATE) {
1001 mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
1002
San Mehat4270e1e2010-01-29 05:32:19 -08001003 } else {
Jeff Sharkey48877892015-03-18 11:27:19 -07001004 Slog.d(TAG, "Skipping automatic mounting of " + vol);
San Mehat4270e1e2010-01-29 05:32:19 -08001005 }
1006 }
1007
Jeff Sharkeye6c04f92015-04-18 21:38:05 -07001008 private boolean isBroadcastWorthy(VolumeInfo vol) {
1009 switch (vol.getType()) {
1010 case VolumeInfo.TYPE_PUBLIC:
1011 case VolumeInfo.TYPE_EMULATED:
1012 break;
1013 default:
1014 return false;
1015 }
1016
1017 switch (vol.getState()) {
1018 case VolumeInfo.STATE_MOUNTED:
1019 case VolumeInfo.STATE_MOUNTED_READ_ONLY:
1020 case VolumeInfo.STATE_EJECTING:
1021 case VolumeInfo.STATE_UNMOUNTED:
1022 break;
1023 default:
1024 return false;
1025 }
1026
1027 return true;
1028 }
1029
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001030 private void onVolumeStateChangedLocked(VolumeInfo vol, int oldState, int newState) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001031 // Remember that we saw this volume so we're ready to accept user
1032 // metadata, or so we can annoy them when a private volume is ejected
1033 if (vol.isMountedReadable() && !TextUtils.isEmpty(vol.fsUuid)) {
1034 if (!mRecords.containsKey(vol.fsUuid)) {
1035 final VolumeRecord rec = new VolumeRecord(vol.type, vol.fsUuid);
1036 if (vol.type == VolumeInfo.TYPE_PRIVATE) {
1037 rec.nickname = vol.disk.getDescription();
1038 }
1039 mRecords.put(rec.fsUuid, rec);
1040 writeSettingsLocked();
1041 }
1042 }
1043
Jeff Sharkeye6c04f92015-04-18 21:38:05 -07001044 mCallbacks.notifyVolumeStateChanged(vol, oldState, newState);
1045
1046 if (isBroadcastWorthy(vol)) {
1047 final Intent intent = new Intent(VolumeInfo.ACTION_VOLUME_STATE_CHANGED);
1048 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Jeff Sharkey620b32b2015-04-23 19:36:02 -07001049 mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
1050 android.Manifest.permission.WRITE_MEDIA_STORAGE);
Jeff Sharkeye6c04f92015-04-18 21:38:05 -07001051 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001052
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001053 final String oldStateEnv = VolumeInfo.getEnvironmentForState(oldState);
1054 final String newStateEnv = VolumeInfo.getEnvironmentForState(newState);
Emily Bernier92aa5a22014-07-07 10:11:48 -04001055
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001056 if (!Objects.equals(oldStateEnv, newStateEnv)) {
1057 // Kick state changed event towards all started users. Any users
1058 // started after this point will trigger additional
1059 // user-specific broadcasts.
1060 for (int userId : mStartedUsers) {
1061 if (vol.isVisibleToUser(userId)) {
1062 final StorageVolume userVol = vol.buildStorageVolume(mContext, userId);
1063 mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget();
Jeff Sharkey48877892015-03-18 11:27:19 -07001064
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001065 mCallbacks.notifyStorageStateChanged(userVol.getPath(), oldStateEnv,
1066 newStateEnv);
San Mehat4270e1e2010-01-29 05:32:19 -08001067 }
1068 }
1069 }
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001070
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07001071 if (vol.type == VolumeInfo.TYPE_PUBLIC && vol.state == VolumeInfo.STATE_EJECTING) {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001072 // TODO: this should eventually be handled by new ObbVolume state changes
1073 /*
1074 * Some OBBs might have been unmounted when this volume was
1075 * unmounted, so send a message to the handler to let it know to
1076 * remove those from the list of mounted OBBS.
1077 */
1078 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(
1079 OBB_FLUSH_MOUNT_STATE, vol.path));
1080 }
San Mehat4270e1e2010-01-29 05:32:19 -08001081 }
1082
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001083 private void onMoveStatusLocked(int status) {
1084 if (mMoveCallback == null) {
1085 Slog.w(TAG, "Odd, status but no move requested");
1086 return;
1087 }
1088
1089 // TODO: estimate remaining time
1090 try {
Jeff Sharkey50a05452015-04-29 11:24:52 -07001091 mMoveCallback.onStatusChanged(-1, status, -1);
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001092 } catch (RemoteException ignored) {
1093 }
1094
1095 // We've finished copying and we're about to clean up old data, so
1096 // remember that move was successful if we get rebooted
1097 if (status == MOVE_STATUS_COPY_FINISHED) {
1098 Slog.d(TAG, "Move to " + mMoveTargetUuid + " copy phase finshed; persisting");
1099
1100 mPrimaryStorageUuid = mMoveTargetUuid;
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001101 writeSettingsLocked();
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001102 }
1103
1104 if (PackageManager.isMoveStatusFinished(status)) {
1105 Slog.d(TAG, "Move to " + mMoveTargetUuid + " finished with status " + status);
1106
1107 mMoveCallback = null;
1108 mMoveTargetUuid = null;
1109 }
1110 }
1111
Jeff Sharkey48877892015-03-18 11:27:19 -07001112 private void enforcePermission(String perm) {
1113 mContext.enforceCallingOrSelfPermission(perm, perm);
Mike Lockwooda5250c92011-05-23 13:44:04 -04001114 }
1115
Jeff Sharkey48877892015-03-18 11:27:19 -07001116 private void enforceUserRestriction(String restriction) {
Emily Bernier92aa5a22014-07-07 10:11:48 -04001117 UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
Jeff Sharkey48877892015-03-18 11:27:19 -07001118 if (um.hasUserRestriction(restriction, Binder.getCallingUserHandle())) {
Emily Bernier92aa5a22014-07-07 10:11:48 -04001119 throw new SecurityException("User has restriction " + restriction);
1120 }
1121 }
1122
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001123 /**
San Mehat207e5382010-02-04 20:46:54 -08001124 * Constructs a new MountService instance
1125 *
1126 * @param context Binder context for this service
1127 */
1128 public MountService(Context context) {
Christopher Tated417d622013-08-19 16:14:25 -07001129 sSelf = this;
1130
San Mehat207e5382010-02-04 20:46:54 -08001131 mContext = context;
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001132 mCallbacks = new Callbacks(FgThread.get().getLooper());
San Mehat207e5382010-02-04 20:46:54 -08001133
San Mehat207e5382010-02-04 20:46:54 -08001134 // XXX: This will go away soon in favor of IMountServiceObserver
1135 mPms = (PackageManagerService) ServiceManager.getService("package");
1136
Dianne Hackbornefa92b22013-05-03 14:11:43 -07001137 HandlerThread hthread = new HandlerThread(TAG);
1138 hthread.start();
1139 mHandler = new MountServiceHandler(hthread.getLooper());
Daniel Sandler5f27ef42010-03-16 15:42:02 -04001140
Kenny Roota02b8b02010-08-05 16:14:17 -07001141 // Add OBB Action Handler to MountService thread.
Dianne Hackborn8d044e82013-04-30 17:24:15 -07001142 mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper());
Kenny Roota02b8b02010-08-05 16:14:17 -07001143
Christopher Tate7265abe2014-11-21 13:54:45 -08001144 // Initialize the last-fstrim tracking if necessary
1145 File dataDir = Environment.getDataDirectory();
1146 File systemDir = new File(dataDir, "system");
1147 mLastMaintenanceFile = new File(systemDir, LAST_FSTRIM_FILE);
1148 if (!mLastMaintenanceFile.exists()) {
1149 // Not setting mLastMaintenance here means that we will force an
1150 // fstrim during reboot following the OTA that installs this code.
1151 try {
1152 (new FileOutputStream(mLastMaintenanceFile)).close();
1153 } catch (IOException e) {
1154 Slog.e(TAG, "Unable to create fstrim record " + mLastMaintenanceFile.getPath());
1155 }
1156 } else {
1157 mLastMaintenance = mLastMaintenanceFile.lastModified();
1158 }
1159
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001160 mSettingsFile = new AtomicFile(
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001161 new File(Environment.getSystemSecureDirectory(), "storage.xml"));
1162
1163 synchronized (mLock) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001164 readSettingsLocked();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001165 }
1166
Marco Nelissenc34ebce2010-02-18 13:39:41 -08001167 /*
Kenny Root305bcbf2010-09-03 07:56:38 -07001168 * Create the connection to vold with a maximum queue of twice the
1169 * amount of containers we'd ever expect to have. This keeps an
1170 * "asec list" from blocking a thread repeatedly.
1171 */
Dianne Hackborn77b987f2014-02-26 16:20:52 -08001172 mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25,
1173 null);
Jeff Sharkey48877892015-03-18 11:27:19 -07001174 mConnector.setDebug(true);
Kenny Root51a573c2012-05-17 13:30:28 -07001175
Kenny Root305bcbf2010-09-03 07:56:38 -07001176 Thread thread = new Thread(mConnector, VOLD_TAG);
San Mehat207e5382010-02-04 20:46:54 -08001177 thread.start();
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -07001178
Kenny Root07714d42011-08-17 17:49:28 -07001179 // Add ourself to the Watchdog monitors if enabled.
1180 if (WATCHDOG_ENABLE) {
1181 Watchdog.getInstance().addMonitor(this);
1182 }
San Mehat207e5382010-02-04 20:46:54 -08001183 }
1184
Jeff Sharkey56e62932015-03-21 20:41:00 -07001185 private void systemReady() {
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001186 mSystemReady = true;
1187 mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget();
1188 }
1189
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001190 private void readSettingsLocked() {
1191 mRecords.clear();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001192
1193 FileInputStream fis = null;
1194 try {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001195 fis = mSettingsFile.openRead();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001196 final XmlPullParser in = Xml.newPullParser();
1197 in.setInput(fis, null);
1198
1199 int type;
1200 while ((type = in.next()) != END_DOCUMENT) {
1201 if (type == START_TAG) {
1202 final String tag = in.getName();
Jeff Sharkey620b32b2015-04-23 19:36:02 -07001203 if (TAG_VOLUMES.equals(tag)) {
1204 final int version = readIntAttribute(in, ATTR_VERSION, VERSION_INIT);
1205 if (version >= VERSION_ADD_PRIMARY) {
1206 mPrimaryStorageUuid = readStringAttribute(in,
1207 ATTR_PRIMARY_STORAGE_UUID);
1208 } else {
1209 if (SystemProperties.getBoolean(StorageManager.PROP_PRIMARY_PHYSICAL,
1210 false)) {
1211 mPrimaryStorageUuid = StorageManager.UUID_PRIMARY_PHYSICAL;
1212 } else {
1213 mPrimaryStorageUuid = StorageManager.UUID_PRIVATE_INTERNAL;
1214 }
1215 }
1216
1217 } else if (TAG_VOLUME.equals(tag)) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001218 final VolumeRecord rec = readVolumeRecord(in);
1219 mRecords.put(rec.fsUuid, rec);
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001220 }
1221 }
1222 }
1223 } catch (FileNotFoundException e) {
1224 // Missing metadata is okay, probably first boot
1225 } catch (IOException e) {
1226 Slog.wtf(TAG, "Failed reading metadata", e);
1227 } catch (XmlPullParserException e) {
1228 Slog.wtf(TAG, "Failed reading metadata", e);
1229 } finally {
1230 IoUtils.closeQuietly(fis);
1231 }
1232 }
1233
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001234 private void writeSettingsLocked() {
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001235 FileOutputStream fos = null;
1236 try {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001237 fos = mSettingsFile.startWrite();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001238
1239 XmlSerializer out = new FastXmlSerializer();
1240 out.setOutput(fos, "utf-8");
1241 out.startDocument(null, true);
1242 out.startTag(null, TAG_VOLUMES);
Jeff Sharkey620b32b2015-04-23 19:36:02 -07001243 writeIntAttribute(out, ATTR_VERSION, VERSION_ADD_PRIMARY);
1244 writeStringAttribute(out, ATTR_PRIMARY_STORAGE_UUID, mPrimaryStorageUuid);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001245 final int size = mRecords.size();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001246 for (int i = 0; i < size; i++) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001247 final VolumeRecord rec = mRecords.valueAt(i);
1248 writeVolumeRecord(out, rec);
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001249 }
1250 out.endTag(null, TAG_VOLUMES);
1251 out.endDocument();
1252
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001253 mSettingsFile.finishWrite(fos);
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001254 } catch (IOException e) {
1255 if (fos != null) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001256 mSettingsFile.failWrite(fos);
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001257 }
1258 }
1259 }
1260
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001261 public static VolumeRecord readVolumeRecord(XmlPullParser in) throws IOException {
1262 final int type = readIntAttribute(in, ATTR_TYPE);
1263 final String fsUuid = readStringAttribute(in, ATTR_FS_UUID);
1264 final VolumeRecord meta = new VolumeRecord(type, fsUuid);
1265 meta.nickname = readStringAttribute(in, ATTR_NICKNAME);
1266 meta.userFlags = readIntAttribute(in, ATTR_USER_FLAGS);
1267 return meta;
1268 }
1269
1270 public static void writeVolumeRecord(XmlSerializer out, VolumeRecord rec) throws IOException {
1271 out.startTag(null, TAG_VOLUME);
1272 writeIntAttribute(out, ATTR_TYPE, rec.type);
1273 writeStringAttribute(out, ATTR_FS_UUID, rec.fsUuid);
1274 writeStringAttribute(out, ATTR_NICKNAME, rec.nickname);
1275 writeIntAttribute(out, ATTR_USER_FLAGS, rec.userFlags);
1276 out.endTag(null, TAG_VOLUME);
1277 }
1278
San Mehat207e5382010-02-04 20:46:54 -08001279 /**
San Mehat4270e1e2010-01-29 05:32:19 -08001280 * Exposed API calls below here
1281 */
1282
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001283 @Override
San Mehat4270e1e2010-01-29 05:32:19 -08001284 public void registerListener(IMountServiceListener listener) {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001285 mCallbacks.register(listener);
San Mehat4270e1e2010-01-29 05:32:19 -08001286 }
1287
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001288 @Override
San Mehat4270e1e2010-01-29 05:32:19 -08001289 public void unregisterListener(IMountServiceListener listener) {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001290 mCallbacks.unregister(listener);
San Mehat4270e1e2010-01-29 05:32:19 -08001291 }
1292
Jeff Sharkey48877892015-03-18 11:27:19 -07001293 @Override
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -08001294 public void shutdown(final IMountShutdownObserver observer) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001295 enforcePermission(android.Manifest.permission.SHUTDOWN);
San Mehat4270e1e2010-01-29 05:32:19 -08001296
San Mehata5078592010-03-25 09:36:54 -07001297 Slog.i(TAG, "Shutting down");
Jeff Sharkey48877892015-03-18 11:27:19 -07001298 mHandler.obtainMessage(H_SHUTDOWN, observer).sendToTarget();
San Mehat4270e1e2010-01-29 05:32:19 -08001299 }
1300
Jeff Sharkey48877892015-03-18 11:27:19 -07001301 @Override
San Mehatb1043402010-02-05 08:26:50 -08001302 public boolean isUsbMassStorageConnected() {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001303 throw new UnsupportedOperationException();
San Mehatb1043402010-02-05 08:26:50 -08001304 }
1305
Jeff Sharkey48877892015-03-18 11:27:19 -07001306 @Override
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001307 public void setUsbMassStorageEnabled(boolean enable) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001308 throw new UnsupportedOperationException();
San Mehatb1043402010-02-05 08:26:50 -08001309 }
1310
Jeff Sharkey48877892015-03-18 11:27:19 -07001311 @Override
San Mehatb1043402010-02-05 08:26:50 -08001312 public boolean isUsbMassStorageEnabled() {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001313 throw new UnsupportedOperationException();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001314 }
Jason parks9ed98bc2011-01-17 09:58:35 -06001315
Jeff Sharkey48877892015-03-18 11:27:19 -07001316 @Override
San Mehat4270e1e2010-01-29 05:32:19 -08001317 public String getVolumeState(String mountPoint) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001318 throw new UnsupportedOperationException();
San Mehat7fd0fee2009-12-17 07:12:23 -08001319 }
1320
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001321 @Override
Kenny Roote1ff2142010-10-12 11:20:01 -07001322 public boolean isExternalStorageEmulated() {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001323 throw new UnsupportedOperationException();
Kenny Roote1ff2142010-10-12 11:20:01 -07001324 }
1325
Jeff Sharkey48877892015-03-18 11:27:19 -07001326 @Override
San Mehat4270e1e2010-01-29 05:32:19 -08001327 public int mountVolume(String path) {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001328 mount(findVolumeIdForPath(path));
1329 return 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001330 }
1331
Jeff Sharkey48877892015-03-18 11:27:19 -07001332 @Override
Ben Komalo13c71972011-09-07 16:35:56 -07001333 public void unmountVolume(String path, boolean force, boolean removeEncryption) {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001334 unmount(findVolumeIdForPath(path));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001335 }
1336
Jeff Sharkey48877892015-03-18 11:27:19 -07001337 @Override
San Mehat4270e1e2010-01-29 05:32:19 -08001338 public int formatVolume(String path) {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001339 format(findVolumeIdForPath(path));
1340 return 0;
1341 }
1342
1343 @Override
1344 public void mount(String volId) {
1345 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1346 waitForReady();
1347
1348 final VolumeInfo vol = findVolumeById(volId);
1349 if (vol.type == VolumeInfo.TYPE_PUBLIC || vol.type == VolumeInfo.TYPE_PRIVATE) {
1350 enforceUserRestriction(UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA);
1351 }
1352 try {
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07001353 mConnector.execute("volume", "mount", vol.id, vol.mountFlags, vol.mountUserId);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001354 } catch (NativeDaemonConnectorException e) {
1355 throw e.rethrowAsParcelableException();
1356 }
1357 }
1358
1359 @Override
1360 public void unmount(String volId) {
1361 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1362 waitForReady();
1363
1364 final VolumeInfo vol = findVolumeById(volId);
1365
1366 // TODO: expand PMS to know about multiple volumes
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001367 if (vol.isPrimaryPhysical()) {
1368 final long ident = Binder.clearCallingIdentity();
1369 try {
1370 synchronized (mUnmountLock) {
1371 mUnmountSignal = new CountDownLatch(1);
1372 mPms.updateExternalMediaStatus(false, true);
1373 waitForLatch(mUnmountSignal, "mUnmountSignal");
1374 mUnmountSignal = null;
1375 }
1376 } finally {
1377 Binder.restoreCallingIdentity(ident);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001378 }
1379 }
1380
1381 try {
1382 mConnector.execute("volume", "unmount", vol.id);
1383 } catch (NativeDaemonConnectorException e) {
1384 throw e.rethrowAsParcelableException();
1385 }
1386 }
1387
1388 @Override
1389 public void format(String volId) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001390 enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
San Mehat207e5382010-02-04 20:46:54 -08001391 waitForReady();
San Mehat5b77dab2010-01-26 13:28:50 -08001392
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001393 final VolumeInfo vol = findVolumeById(volId);
1394 try {
1395 mConnector.execute("volume", "format", vol.id);
1396 } catch (NativeDaemonConnectorException e) {
1397 throw e.rethrowAsParcelableException();
Jeff Sharkey48877892015-03-18 11:27:19 -07001398 }
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001399 }
1400
1401 @Override
1402 public void partitionPublic(String diskId) {
1403 enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
1404 waitForReady();
1405
Jeff Sharkeyeba260d2015-04-19 14:35:16 -07001406 final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001407 try {
1408 mConnector.execute("volume", "partition", diskId, "public");
1409 } catch (NativeDaemonConnectorException e) {
1410 throw e.rethrowAsParcelableException();
1411 }
Jeff Sharkeyeba260d2015-04-19 14:35:16 -07001412 waitForLatch(latch, "partitionPublic");
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001413 }
1414
1415 @Override
1416 public void partitionPrivate(String diskId) {
1417 enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
1418 waitForReady();
1419
Jeff Sharkeyeba260d2015-04-19 14:35:16 -07001420 final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001421 try {
1422 mConnector.execute("volume", "partition", diskId, "private");
1423 } catch (NativeDaemonConnectorException e) {
1424 throw e.rethrowAsParcelableException();
1425 }
Jeff Sharkeyeba260d2015-04-19 14:35:16 -07001426 waitForLatch(latch, "partitionPrivate");
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001427 }
1428
1429 @Override
1430 public void partitionMixed(String diskId, int ratio) {
1431 enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
1432 waitForReady();
1433
Jeff Sharkeyeba260d2015-04-19 14:35:16 -07001434 final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001435 try {
1436 mConnector.execute("volume", "partition", diskId, "mixed", ratio);
1437 } catch (NativeDaemonConnectorException e) {
1438 throw e.rethrowAsParcelableException();
1439 }
Jeff Sharkeyeba260d2015-04-19 14:35:16 -07001440 waitForLatch(latch, "partitionMixed");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001441 }
1442
Jeff Sharkey48877892015-03-18 11:27:19 -07001443 @Override
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001444 public void setVolumeNickname(String fsUuid, String nickname) {
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001445 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1446 waitForReady();
1447
Jeff Sharkey50a05452015-04-29 11:24:52 -07001448 Preconditions.checkNotNull(fsUuid);
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001449 synchronized (mLock) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001450 final VolumeRecord rec = mRecords.get(fsUuid);
1451 rec.nickname = nickname;
Jeff Sharkey50a05452015-04-29 11:24:52 -07001452 mCallbacks.notifyVolumeRecordChanged(rec);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001453 writeSettingsLocked();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001454 }
1455 }
1456
1457 @Override
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001458 public void setVolumeUserFlags(String fsUuid, int flags, int mask) {
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001459 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1460 waitForReady();
1461
Jeff Sharkey50a05452015-04-29 11:24:52 -07001462 Preconditions.checkNotNull(fsUuid);
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001463 synchronized (mLock) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001464 final VolumeRecord rec = mRecords.get(fsUuid);
1465 rec.userFlags = (rec.userFlags & ~mask) | (flags & mask);
Jeff Sharkey50a05452015-04-29 11:24:52 -07001466 mCallbacks.notifyVolumeRecordChanged(rec);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001467 writeSettingsLocked();
1468 }
1469 }
1470
1471 @Override
1472 public void forgetVolume(String fsUuid) {
1473 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1474 waitForReady();
1475
Jeff Sharkey50a05452015-04-29 11:24:52 -07001476 Preconditions.checkNotNull(fsUuid);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001477 synchronized (mLock) {
1478 mRecords.remove(fsUuid);
Jeff Sharkey50a05452015-04-29 11:24:52 -07001479
1480 // TODO: tell vold to forget keys
1481
1482 // If this had been primary storage, revert back to internal and
1483 // reset vold so we bind into new volume into place.
1484 if (Objects.equals(mPrimaryStorageUuid, fsUuid)) {
1485 mPrimaryStorageUuid = StorageManager.UUID_PRIVATE_INTERNAL;
1486 resetIfReadyAndConnected();
1487 }
1488
1489 mCallbacks.notifyVolumeForgotten(fsUuid);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001490 writeSettingsLocked();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001491 }
1492 }
1493
Jeff Sharkey50a05452015-04-29 11:24:52 -07001494 private void forgetAll() {
1495 synchronized (mLock) {
1496 for (int i = 0; i < mRecords.size(); i++) {
1497 final String fsUuid = mRecords.keyAt(i);
1498 mCallbacks.notifyVolumeForgotten(fsUuid);
1499 }
1500
1501 mRecords.clear();
1502 writeSettingsLocked();
1503
1504 mPrimaryStorageUuid = StorageManager.UUID_PRIVATE_INTERNAL;
1505 resetIfReadyAndConnected();
1506 }
1507 }
1508
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001509 @Override
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001510 public String getPrimaryStorageUuid() {
1511 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1512 waitForReady();
1513
Jeff Sharkey620b32b2015-04-23 19:36:02 -07001514 synchronized (mLock) {
1515 return mPrimaryStorageUuid;
1516 }
1517 }
1518
1519 @Override
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001520 public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) {
1521 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1522 waitForReady();
Jeff Sharkey620b32b2015-04-23 19:36:02 -07001523
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001524 synchronized (mLock) {
1525 final VolumeInfo from = Preconditions.checkNotNull(
1526 findStorageForUuid(mPrimaryStorageUuid));
1527 final VolumeInfo to = Preconditions.checkNotNull(
1528 findStorageForUuid(volumeUuid));
1529
1530 if (Objects.equals(from, to)) {
1531 throw new IllegalArgumentException("Primary storage already at " + from);
1532 }
1533
1534 if (mMoveCallback != null) {
1535 throw new IllegalStateException("Move already in progress");
1536 }
1537 mMoveCallback = callback;
1538 mMoveTargetUuid = volumeUuid;
1539
1540 try {
1541 mConnector.execute("volume", "move_storage", from.id, to.id);
1542 } catch (NativeDaemonConnectorException e) {
1543 throw e.rethrowAsParcelableException();
1544 }
Jeff Sharkey620b32b2015-04-23 19:36:02 -07001545 }
1546 }
1547
1548 @Override
Mike Lockwoodecedfdc2011-06-08 15:11:59 -07001549 public int[] getStorageUsers(String path) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001550 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
San Mehatc1b4ce92010-02-16 17:13:03 -08001551 waitForReady();
1552 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001553 final String[] r = NativeDaemonEvent.filterMessageList(
1554 mConnector.executeForList("storage", "users", path),
1555 VoldResponseCode.StorageUsersListResult);
1556
San Mehatc1b4ce92010-02-16 17:13:03 -08001557 // FMT: <pid> <process name>
1558 int[] data = new int[r.length];
1559 for (int i = 0; i < r.length; i++) {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001560 String[] tok = r[i].split(" ");
San Mehatc1b4ce92010-02-16 17:13:03 -08001561 try {
1562 data[i] = Integer.parseInt(tok[0]);
1563 } catch (NumberFormatException nfe) {
San Mehata5078592010-03-25 09:36:54 -07001564 Slog.e(TAG, String.format("Error parsing pid %s", tok[0]));
San Mehatc1b4ce92010-02-16 17:13:03 -08001565 return new int[0];
1566 }
1567 }
1568 return data;
1569 } catch (NativeDaemonConnectorException e) {
San Mehata5078592010-03-25 09:36:54 -07001570 Slog.e(TAG, "Failed to retrieve storage users list", e);
San Mehatc1b4ce92010-02-16 17:13:03 -08001571 return new int[0];
1572 }
1573 }
1574
San Mehatb1043402010-02-05 08:26:50 -08001575 private void warnOnNotMounted() {
Jeff Sharkey48877892015-03-18 11:27:19 -07001576 synchronized (mLock) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001577 for (int i = 0; i < mVolumes.size(); i++) {
1578 final VolumeInfo vol = mVolumes.valueAt(i);
Jeff Sharkey27de30d2015-04-18 16:20:27 -07001579 if (vol.isPrimary() && vol.isMountedWritable()) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001580 // Cool beans, we have a mounted primary volume
1581 return;
1582 }
Jeff Sharkey32ee8312012-09-30 13:21:31 -07001583 }
San Mehatb1043402010-02-05 08:26:50 -08001584 }
Jeff Sharkey48877892015-03-18 11:27:19 -07001585
1586 Slog.w(TAG, "No primary storage mounted!");
San Mehatb1043402010-02-05 08:26:50 -08001587 }
1588
San Mehat4270e1e2010-01-29 05:32:19 -08001589 public String[] getSecureContainerList() {
Jeff Sharkey48877892015-03-18 11:27:19 -07001590 enforcePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat207e5382010-02-04 20:46:54 -08001591 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001592 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001593
San Mehat4270e1e2010-01-29 05:32:19 -08001594 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001595 return NativeDaemonEvent.filterMessageList(
1596 mConnector.executeForList("asec", "list"), VoldResponseCode.AsecListResult);
San Mehat4270e1e2010-01-29 05:32:19 -08001597 } catch (NativeDaemonConnectorException e) {
1598 return new String[0];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001599 }
1600 }
San Mehat36972292010-01-06 11:06:32 -08001601
Kenny Root6dceb882012-04-12 14:23:49 -07001602 public int createSecureContainer(String id, int sizeMb, String fstype, String key,
1603 int ownerUid, boolean external) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001604 enforcePermission(android.Manifest.permission.ASEC_CREATE);
San Mehat207e5382010-02-04 20:46:54 -08001605 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001606 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001607
San Mehatb1043402010-02-05 08:26:50 -08001608 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001609 try {
Jeff Sharkey56cd6462013-06-07 15:09:15 -07001610 mConnector.execute("asec", "create", id, sizeMb, fstype, new SensitiveArg(key),
1611 ownerUid, external ? "1" : "0");
San Mehat4270e1e2010-01-29 05:32:19 -08001612 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001613 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001614 }
San Mehata181b212010-02-11 06:50:20 -08001615
1616 if (rc == StorageResultCode.OperationSucceeded) {
1617 synchronized (mAsecMountSet) {
1618 mAsecMountSet.add(id);
1619 }
1620 }
San Mehat4270e1e2010-01-29 05:32:19 -08001621 return rc;
San Mehat36972292010-01-06 11:06:32 -08001622 }
1623
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001624 @Override
1625 public int resizeSecureContainer(String id, int sizeMb, String key) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001626 enforcePermission(android.Manifest.permission.ASEC_CREATE);
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001627 waitForReady();
1628 warnOnNotMounted();
1629
1630 int rc = StorageResultCode.OperationSucceeded;
1631 try {
1632 mConnector.execute("asec", "resize", id, sizeMb, new SensitiveArg(key));
1633 } catch (NativeDaemonConnectorException e) {
1634 rc = StorageResultCode.OperationFailedInternalError;
1635 }
1636 return rc;
1637 }
1638
San Mehat4270e1e2010-01-29 05:32:19 -08001639 public int finalizeSecureContainer(String id) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001640 enforcePermission(android.Manifest.permission.ASEC_CREATE);
San Mehatb1043402010-02-05 08:26:50 -08001641 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001642
San Mehatb1043402010-02-05 08:26:50 -08001643 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001644 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001645 mConnector.execute("asec", "finalize", id);
San Mehata181b212010-02-11 06:50:20 -08001646 /*
1647 * Finalization does a remount, so no need
1648 * to update mAsecMountSet
1649 */
San Mehat4270e1e2010-01-29 05:32:19 -08001650 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001651 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001652 }
San Mehat4270e1e2010-01-29 05:32:19 -08001653 return rc;
San Mehat36972292010-01-06 11:06:32 -08001654 }
1655
Kenny Root6dceb882012-04-12 14:23:49 -07001656 public int fixPermissionsSecureContainer(String id, int gid, String filename) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001657 enforcePermission(android.Manifest.permission.ASEC_CREATE);
Kenny Root6dceb882012-04-12 14:23:49 -07001658 warnOnNotMounted();
1659
1660 int rc = StorageResultCode.OperationSucceeded;
1661 try {
1662 mConnector.execute("asec", "fixperms", id, gid, filename);
1663 /*
1664 * Fix permissions does a remount, so no need to update
1665 * mAsecMountSet
1666 */
1667 } catch (NativeDaemonConnectorException e) {
1668 rc = StorageResultCode.OperationFailedInternalError;
1669 }
1670 return rc;
1671 }
1672
San Mehatd9709982010-02-18 11:43:03 -08001673 public int destroySecureContainer(String id, boolean force) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001674 enforcePermission(android.Manifest.permission.ASEC_DESTROY);
San Mehat207e5382010-02-04 20:46:54 -08001675 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001676 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001677
Kenny Rootaa485402010-09-14 14:49:41 -07001678 /*
1679 * Force a GC to make sure AssetManagers in other threads of the
1680 * system_server are cleaned up. We have to do this since AssetManager
1681 * instances are kept as a WeakReference and it's possible we have files
1682 * open on the external storage.
1683 */
1684 Runtime.getRuntime().gc();
1685
San Mehatb1043402010-02-05 08:26:50 -08001686 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001687 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001688 final Command cmd = new Command("asec", "destroy", id);
1689 if (force) {
1690 cmd.appendArg("force");
1691 }
1692 mConnector.execute(cmd);
San Mehat4270e1e2010-01-29 05:32:19 -08001693 } catch (NativeDaemonConnectorException e) {
San Mehatd9709982010-02-18 11:43:03 -08001694 int code = e.getCode();
1695 if (code == VoldResponseCode.OpFailedStorageBusy) {
1696 rc = StorageResultCode.OperationFailedStorageBusy;
1697 } else {
1698 rc = StorageResultCode.OperationFailedInternalError;
1699 }
San Mehat02735bc2010-01-26 15:18:08 -08001700 }
San Mehata181b212010-02-11 06:50:20 -08001701
1702 if (rc == StorageResultCode.OperationSucceeded) {
1703 synchronized (mAsecMountSet) {
1704 if (mAsecMountSet.contains(id)) {
1705 mAsecMountSet.remove(id);
1706 }
1707 }
1708 }
1709
San Mehat4270e1e2010-01-29 05:32:19 -08001710 return rc;
San Mehat36972292010-01-06 11:06:32 -08001711 }
Jason parks9ed98bc2011-01-17 09:58:35 -06001712
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001713 public int mountSecureContainer(String id, String key, int ownerUid, boolean readOnly) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001714 enforcePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
San Mehat207e5382010-02-04 20:46:54 -08001715 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001716 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001717
San Mehata181b212010-02-11 06:50:20 -08001718 synchronized (mAsecMountSet) {
1719 if (mAsecMountSet.contains(id)) {
1720 return StorageResultCode.OperationFailedStorageMounted;
1721 }
1722 }
1723
San Mehatb1043402010-02-05 08:26:50 -08001724 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001725 try {
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001726 mConnector.execute("asec", "mount", id, new SensitiveArg(key), ownerUid,
1727 readOnly ? "ro" : "rw");
San Mehat4270e1e2010-01-29 05:32:19 -08001728 } catch (NativeDaemonConnectorException e) {
Kenny Rootf0304622010-03-19 19:20:42 -07001729 int code = e.getCode();
1730 if (code != VoldResponseCode.OpFailedStorageBusy) {
1731 rc = StorageResultCode.OperationFailedInternalError;
1732 }
San Mehat02735bc2010-01-26 15:18:08 -08001733 }
San Mehat6cdd9c02010-02-09 14:45:20 -08001734
1735 if (rc == StorageResultCode.OperationSucceeded) {
1736 synchronized (mAsecMountSet) {
1737 mAsecMountSet.add(id);
1738 }
1739 }
San Mehat4270e1e2010-01-29 05:32:19 -08001740 return rc;
San Mehat36972292010-01-06 11:06:32 -08001741 }
1742
San Mehatd9709982010-02-18 11:43:03 -08001743 public int unmountSecureContainer(String id, boolean force) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001744 enforcePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
San Mehat207e5382010-02-04 20:46:54 -08001745 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001746 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001747
San Mehat6cdd9c02010-02-09 14:45:20 -08001748 synchronized (mAsecMountSet) {
1749 if (!mAsecMountSet.contains(id)) {
San Mehata181b212010-02-11 06:50:20 -08001750 return StorageResultCode.OperationFailedStorageNotMounted;
San Mehat6cdd9c02010-02-09 14:45:20 -08001751 }
1752 }
1753
Kenny Rootaa485402010-09-14 14:49:41 -07001754 /*
1755 * Force a GC to make sure AssetManagers in other threads of the
1756 * system_server are cleaned up. We have to do this since AssetManager
1757 * instances are kept as a WeakReference and it's possible we have files
1758 * open on the external storage.
1759 */
1760 Runtime.getRuntime().gc();
1761
San Mehatb1043402010-02-05 08:26:50 -08001762 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001763 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001764 final Command cmd = new Command("asec", "unmount", id);
1765 if (force) {
1766 cmd.appendArg("force");
1767 }
1768 mConnector.execute(cmd);
San Mehat4270e1e2010-01-29 05:32:19 -08001769 } catch (NativeDaemonConnectorException e) {
San Mehatd9709982010-02-18 11:43:03 -08001770 int code = e.getCode();
1771 if (code == VoldResponseCode.OpFailedStorageBusy) {
1772 rc = StorageResultCode.OperationFailedStorageBusy;
1773 } else {
1774 rc = StorageResultCode.OperationFailedInternalError;
1775 }
San Mehat02735bc2010-01-26 15:18:08 -08001776 }
San Mehat6cdd9c02010-02-09 14:45:20 -08001777
1778 if (rc == StorageResultCode.OperationSucceeded) {
1779 synchronized (mAsecMountSet) {
1780 mAsecMountSet.remove(id);
1781 }
1782 }
San Mehat4270e1e2010-01-29 05:32:19 -08001783 return rc;
San Mehat9dba7092010-01-18 06:47:41 -08001784 }
1785
San Mehat6cdd9c02010-02-09 14:45:20 -08001786 public boolean isSecureContainerMounted(String id) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001787 enforcePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat6cdd9c02010-02-09 14:45:20 -08001788 waitForReady();
1789 warnOnNotMounted();
1790
1791 synchronized (mAsecMountSet) {
1792 return mAsecMountSet.contains(id);
1793 }
1794 }
1795
San Mehat4270e1e2010-01-29 05:32:19 -08001796 public int renameSecureContainer(String oldId, String newId) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001797 enforcePermission(android.Manifest.permission.ASEC_RENAME);
San Mehat207e5382010-02-04 20:46:54 -08001798 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001799 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001800
San Mehata181b212010-02-11 06:50:20 -08001801 synchronized (mAsecMountSet) {
San Mehat85451ee2010-02-24 08:54:18 -08001802 /*
Jason parks9ed98bc2011-01-17 09:58:35 -06001803 * Because a mounted container has active internal state which cannot be
San Mehat85451ee2010-02-24 08:54:18 -08001804 * changed while active, we must ensure both ids are not currently mounted.
1805 */
1806 if (mAsecMountSet.contains(oldId) || mAsecMountSet.contains(newId)) {
San Mehata181b212010-02-11 06:50:20 -08001807 return StorageResultCode.OperationFailedStorageMounted;
1808 }
1809 }
1810
San Mehatb1043402010-02-05 08:26:50 -08001811 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001812 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001813 mConnector.execute("asec", "rename", oldId, newId);
San Mehat4270e1e2010-01-29 05:32:19 -08001814 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001815 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001816 }
San Mehata181b212010-02-11 06:50:20 -08001817
San Mehat4270e1e2010-01-29 05:32:19 -08001818 return rc;
San Mehat45f61042010-01-23 08:12:43 -08001819 }
1820
San Mehat4270e1e2010-01-29 05:32:19 -08001821 public String getSecureContainerPath(String id) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001822 enforcePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat207e5382010-02-04 20:46:54 -08001823 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001824 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001825
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001826 final NativeDaemonEvent event;
San Mehat2d66cef2010-03-23 11:12:52 -07001827 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001828 event = mConnector.execute("asec", "path", id);
1829 event.checkCode(VoldResponseCode.AsecPathResult);
1830 return event.getMessage();
San Mehat2d66cef2010-03-23 11:12:52 -07001831 } catch (NativeDaemonConnectorException e) {
1832 int code = e.getCode();
1833 if (code == VoldResponseCode.OpFailedStorageNotFound) {
Fredrik Helmera20c8ef2011-02-09 16:16:10 +01001834 Slog.i(TAG, String.format("Container '%s' not found", id));
1835 return null;
San Mehat22dd86e2010-01-12 12:21:18 -08001836 } else {
San Mehat2d66cef2010-03-23 11:12:52 -07001837 throw new IllegalStateException(String.format("Unexpected response code %d", code));
San Mehat22dd86e2010-01-12 12:21:18 -08001838 }
1839 }
San Mehat22dd86e2010-01-12 12:21:18 -08001840 }
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -07001841
Dianne Hackborn292f8bc2011-06-27 16:27:41 -07001842 public String getSecureContainerFilesystemPath(String id) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001843 enforcePermission(android.Manifest.permission.ASEC_ACCESS);
Dianne Hackborn292f8bc2011-06-27 16:27:41 -07001844 waitForReady();
1845 warnOnNotMounted();
1846
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001847 final NativeDaemonEvent event;
Dianne Hackborn292f8bc2011-06-27 16:27:41 -07001848 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001849 event = mConnector.execute("asec", "fspath", id);
1850 event.checkCode(VoldResponseCode.AsecPathResult);
1851 return event.getMessage();
Dianne Hackborn292f8bc2011-06-27 16:27:41 -07001852 } catch (NativeDaemonConnectorException e) {
1853 int code = e.getCode();
1854 if (code == VoldResponseCode.OpFailedStorageNotFound) {
1855 Slog.i(TAG, String.format("Container '%s' not found", id));
1856 return null;
1857 } else {
1858 throw new IllegalStateException(String.format("Unexpected response code %d", code));
1859 }
1860 }
1861 }
1862
Jeff Sharkey48877892015-03-18 11:27:19 -07001863 @Override
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -07001864 public void finishMediaUpdate() {
Rubin Xucd7a0142015-04-17 23:45:27 +01001865 if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
1866 throw new SecurityException("no permission to call finishMediaUpdate()");
1867 }
Jeff Sharkey48877892015-03-18 11:27:19 -07001868 if (mUnmountSignal != null) {
1869 mUnmountSignal.countDown();
1870 } else {
1871 Slog.w(TAG, "Odd, nobody asked to unmount?");
1872 }
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -07001873 }
Kenny Root02c87302010-07-01 08:10:18 -07001874
Kenny Roota02b8b02010-08-05 16:14:17 -07001875 private boolean isUidOwnerOfPackageOrSystem(String packageName, int callerUid) {
1876 if (callerUid == android.os.Process.SYSTEM_UID) {
1877 return true;
1878 }
1879
Kenny Root02c87302010-07-01 08:10:18 -07001880 if (packageName == null) {
1881 return false;
1882 }
1883
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07001884 final int packageUid = mPms.getPackageUid(packageName, UserHandle.getUserId(callerUid));
Kenny Root02c87302010-07-01 08:10:18 -07001885
1886 if (DEBUG_OBB) {
1887 Slog.d(TAG, "packageName = " + packageName + ", packageUid = " +
1888 packageUid + ", callerUid = " + callerUid);
1889 }
1890
1891 return callerUid == packageUid;
1892 }
1893
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001894 public String getMountedObbPath(String rawPath) {
1895 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
Kenny Rootaf9d6672010-10-08 09:21:39 -07001896
Kenny Root02c87302010-07-01 08:10:18 -07001897 waitForReady();
1898 warnOnNotMounted();
1899
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001900 final ObbState state;
Rubin Xucd7a0142015-04-17 23:45:27 +01001901 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001902 state = mObbPathToStateMap.get(rawPath);
1903 }
1904 if (state == null) {
1905 Slog.w(TAG, "Failed to find OBB mounted at " + rawPath);
1906 return null;
1907 }
1908
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001909 final NativeDaemonEvent event;
Kenny Root02c87302010-07-01 08:10:18 -07001910 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001911 event = mConnector.execute("obb", "path", state.voldPath);
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001912 event.checkCode(VoldResponseCode.AsecPathResult);
1913 return event.getMessage();
Kenny Root02c87302010-07-01 08:10:18 -07001914 } catch (NativeDaemonConnectorException e) {
1915 int code = e.getCode();
1916 if (code == VoldResponseCode.OpFailedStorageNotFound) {
Kenny Roota02b8b02010-08-05 16:14:17 -07001917 return null;
Kenny Root02c87302010-07-01 08:10:18 -07001918 } else {
1919 throw new IllegalStateException(String.format("Unexpected response code %d", code));
1920 }
1921 }
1922 }
1923
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001924 @Override
1925 public boolean isObbMounted(String rawPath) {
1926 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
Kenny Rootaf9d6672010-10-08 09:21:39 -07001927 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001928 return mObbPathToStateMap.containsKey(rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07001929 }
Kenny Root02c87302010-07-01 08:10:18 -07001930 }
1931
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001932 @Override
1933 public void mountObb(
1934 String rawPath, String canonicalPath, String key, IObbActionListener token, int nonce) {
1935 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
1936 Preconditions.checkNotNull(canonicalPath, "canonicalPath cannot be null");
1937 Preconditions.checkNotNull(token, "token cannot be null");
Kenny Rootaf9d6672010-10-08 09:21:39 -07001938
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001939 final int callingUid = Binder.getCallingUid();
1940 final ObbState obbState = new ObbState(rawPath, canonicalPath, callingUid, token, nonce);
1941 final ObbAction action = new MountObbAction(obbState, key, callingUid);
Kenny Roota02b8b02010-08-05 16:14:17 -07001942 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
1943
1944 if (DEBUG_OBB)
1945 Slog.i(TAG, "Send to OBB handler: " + action.toString());
Kenny Root02c87302010-07-01 08:10:18 -07001946 }
1947
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001948 @Override
1949 public void unmountObb(String rawPath, boolean force, IObbActionListener token, int nonce) {
1950 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
1951
1952 final ObbState existingState;
Rubin Xucd7a0142015-04-17 23:45:27 +01001953 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001954 existingState = mObbPathToStateMap.get(rawPath);
Kenny Rootf1121dc2010-09-29 07:30:53 -07001955 }
1956
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001957 if (existingState != null) {
1958 // TODO: separate state object from request data
1959 final int callingUid = Binder.getCallingUid();
1960 final ObbState newState = new ObbState(
1961 rawPath, existingState.canonicalPath, callingUid, token, nonce);
1962 final ObbAction action = new UnmountObbAction(newState, force);
1963 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
Kenny Root02c87302010-07-01 08:10:18 -07001964
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001965 if (DEBUG_OBB)
1966 Slog.i(TAG, "Send to OBB handler: " + action.toString());
1967 } else {
1968 Slog.w(TAG, "Unknown OBB mount at " + rawPath);
1969 }
Kenny Roota02b8b02010-08-05 16:14:17 -07001970 }
1971
Ben Komalo444eca22011-09-01 15:17:44 -07001972 @Override
1973 public int getEncryptionState() {
1974 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
1975 "no permission to access the crypt keeper");
1976
1977 waitForReady();
1978
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001979 final NativeDaemonEvent event;
Ben Komalo444eca22011-09-01 15:17:44 -07001980 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001981 event = mConnector.execute("cryptfs", "cryptocomplete");
1982 return Integer.parseInt(event.getMessage());
Ben Komalo444eca22011-09-01 15:17:44 -07001983 } catch (NumberFormatException e) {
1984 // Bad result - unexpected.
1985 Slog.w(TAG, "Unable to parse result from cryptfs cryptocomplete");
1986 return ENCRYPTION_STATE_ERROR_UNKNOWN;
1987 } catch (NativeDaemonConnectorException e) {
1988 // Something bad happened.
1989 Slog.w(TAG, "Error in communicating with cryptfs in validating");
1990 return ENCRYPTION_STATE_ERROR_UNKNOWN;
1991 }
1992 }
1993
Narayan Kamath1653b1d2014-12-17 13:40:36 +00001994 private static String toHex(String password) {
Paul Lawrence8e397362014-01-27 15:22:30 -08001995 if (password == null) {
Narayan Kamath78108a32014-12-16 12:56:23 +00001996 return "";
Paul Lawrence8e397362014-01-27 15:22:30 -08001997 }
1998 byte[] bytes = password.getBytes(StandardCharsets.UTF_8);
Narayan Kamath78108a32014-12-16 12:56:23 +00001999 return new String(HexEncoding.encode(bytes));
Paul Lawrence8e397362014-01-27 15:22:30 -08002000 }
2001
Narayan Kamath1653b1d2014-12-17 13:40:36 +00002002 private static String fromHex(String hexPassword) throws IllegalArgumentException {
Paul Lawrence945490c2014-03-27 16:37:28 +00002003 if (hexPassword == null) {
2004 return null;
2005 }
2006
Narayan Kamath1653b1d2014-12-17 13:40:36 +00002007 final byte[] bytes = HexEncoding.decode(hexPassword.toCharArray(), false);
Narayan Kamath78108a32014-12-16 12:56:23 +00002008 return new String(bytes, StandardCharsets.UTF_8);
Paul Lawrence945490c2014-03-27 16:37:28 +00002009 }
2010
Ben Komalo444eca22011-09-01 15:17:44 -07002011 @Override
Jason parks5af0b912010-11-29 09:05:25 -06002012 public int decryptStorage(String password) {
Jason parksf7b3cd42011-01-27 09:28:25 -06002013 if (TextUtils.isEmpty(password)) {
2014 throw new IllegalArgumentException("password cannot be empty");
Jason parks5af0b912010-11-29 09:05:25 -06002015 }
2016
Jason parks8888c592011-01-20 22:46:41 -06002017 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2018 "no permission to access the crypt keeper");
Jason parks5af0b912010-11-29 09:05:25 -06002019
2020 waitForReady();
2021
2022 if (DEBUG_EVENTS) {
2023 Slog.i(TAG, "decrypting storage...");
2024 }
2025
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002026 final NativeDaemonEvent event;
Jason parks5af0b912010-11-29 09:05:25 -06002027 try {
Paul Lawrence8e397362014-01-27 15:22:30 -08002028 event = mConnector.execute("cryptfs", "checkpw", new SensitiveArg(toHex(password)));
Jason parks9ed98bc2011-01-17 09:58:35 -06002029
Fredrik Roubertda6aedf2011-12-20 17:34:43 +01002030 final int code = Integer.parseInt(event.getMessage());
Jason parks9ed98bc2011-01-17 09:58:35 -06002031 if (code == 0) {
2032 // Decrypt was successful. Post a delayed message before restarting in order
2033 // to let the UI to clear itself
2034 mHandler.postDelayed(new Runnable() {
2035 public void run() {
Jeff Sharkey31c6e482011-11-18 17:09:01 -08002036 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002037 mConnector.execute("cryptfs", "restart");
Jeff Sharkey31c6e482011-11-18 17:09:01 -08002038 } catch (NativeDaemonConnectorException e) {
2039 Slog.e(TAG, "problem executing in background", e);
2040 }
Jason parks9ed98bc2011-01-17 09:58:35 -06002041 }
Jason parksf7b3cd42011-01-27 09:28:25 -06002042 }, 1000); // 1 second
Jason parks9ed98bc2011-01-17 09:58:35 -06002043 }
2044
2045 return code;
Jason parks5af0b912010-11-29 09:05:25 -06002046 } catch (NativeDaemonConnectorException e) {
2047 // Decryption failed
2048 return e.getCode();
2049 }
Jason parks5af0b912010-11-29 09:05:25 -06002050 }
2051
Paul Lawrence46791e72014-04-03 09:10:26 -07002052 public int encryptStorage(int type, String password) {
2053 if (TextUtils.isEmpty(password) && type != StorageManager.CRYPT_TYPE_DEFAULT) {
Jason parksf7b3cd42011-01-27 09:28:25 -06002054 throw new IllegalArgumentException("password cannot be empty");
Jason parks56aa5322011-01-07 09:01:15 -06002055 }
2056
Jason parks8888c592011-01-20 22:46:41 -06002057 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2058 "no permission to access the crypt keeper");
Jason parks56aa5322011-01-07 09:01:15 -06002059
2060 waitForReady();
2061
2062 if (DEBUG_EVENTS) {
Jason parks8888c592011-01-20 22:46:41 -06002063 Slog.i(TAG, "encrypting storage...");
Jason parks56aa5322011-01-07 09:01:15 -06002064 }
2065
2066 try {
Paul Lawrence46791e72014-04-03 09:10:26 -07002067 mConnector.execute("cryptfs", "enablecrypto", "inplace", CRYPTO_TYPES[type],
Paul Lawrence8e397362014-01-27 15:22:30 -08002068 new SensitiveArg(toHex(password)));
Jason parks56aa5322011-01-07 09:01:15 -06002069 } catch (NativeDaemonConnectorException e) {
2070 // Encryption failed
2071 return e.getCode();
2072 }
2073
2074 return 0;
2075 }
2076
Paul Lawrence8e397362014-01-27 15:22:30 -08002077 /** Set the password for encrypting the master key.
2078 * @param type One of the CRYPTO_TYPE_XXX consts defined in StorageManager.
2079 * @param password The password to set.
2080 */
2081 public int changeEncryptionPassword(int type, String password) {
Jason parksf7b3cd42011-01-27 09:28:25 -06002082 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2083 "no permission to access the crypt keeper");
2084
2085 waitForReady();
2086
2087 if (DEBUG_EVENTS) {
2088 Slog.i(TAG, "changing encryption password...");
2089 }
2090
2091 try {
Svetoslava6711ff2014-10-17 11:38:06 -07002092 NativeDaemonEvent event = mConnector.execute("cryptfs", "changepw", CRYPTO_TYPES[type],
Svetoslav16e4a1a2014-09-29 18:16:20 -07002093 new SensitiveArg(toHex(password)));
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002094 return Integer.parseInt(event.getMessage());
Jason parksf7b3cd42011-01-27 09:28:25 -06002095 } catch (NativeDaemonConnectorException e) {
2096 // Encryption failed
2097 return e.getCode();
2098 }
2099 }
2100
Christopher Tate32418be2011-10-10 13:51:12 -07002101 /**
2102 * Validate a user-supplied password string with cryptfs
2103 */
2104 @Override
2105 public int verifyEncryptionPassword(String password) throws RemoteException {
2106 // Only the system process is permitted to validate passwords
2107 if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
2108 throw new SecurityException("no permission to access the crypt keeper");
2109 }
2110
2111 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2112 "no permission to access the crypt keeper");
2113
2114 if (TextUtils.isEmpty(password)) {
2115 throw new IllegalArgumentException("password cannot be empty");
2116 }
2117
2118 waitForReady();
2119
2120 if (DEBUG_EVENTS) {
2121 Slog.i(TAG, "validating encryption password...");
2122 }
2123
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002124 final NativeDaemonEvent event;
Christopher Tate32418be2011-10-10 13:51:12 -07002125 try {
Paul Lawrence8e397362014-01-27 15:22:30 -08002126 event = mConnector.execute("cryptfs", "verifypw", new SensitiveArg(toHex(password)));
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002127 Slog.i(TAG, "cryptfs verifypw => " + event.getMessage());
2128 return Integer.parseInt(event.getMessage());
Christopher Tate32418be2011-10-10 13:51:12 -07002129 } catch (NativeDaemonConnectorException e) {
2130 // Encryption failed
2131 return e.getCode();
2132 }
2133 }
2134
Paul Lawrence8e397362014-01-27 15:22:30 -08002135 /**
2136 * Get the type of encryption used to encrypt the master key.
2137 * @return The type, one of the CRYPT_TYPE_XXX consts from StorageManager.
2138 */
2139 @Override
Svetoslav16e4a1a2014-09-29 18:16:20 -07002140 public int getPasswordType() {
Paul Lawrence8e397362014-01-27 15:22:30 -08002141
2142 waitForReady();
2143
2144 final NativeDaemonEvent event;
2145 try {
2146 event = mConnector.execute("cryptfs", "getpwtype");
2147 for (int i = 0; i < CRYPTO_TYPES.length; ++i) {
2148 if (CRYPTO_TYPES[i].equals(event.getMessage()))
2149 return i;
2150 }
2151
2152 throw new IllegalStateException("unexpected return from cryptfs");
2153 } catch (NativeDaemonConnectorException e) {
2154 throw e.rethrowAsParcelableException();
2155 }
2156 }
2157
Paul Lawrencee51dcf92014-03-18 10:56:00 -07002158 /**
2159 * Set a field in the crypto header.
2160 * @param field field to set
2161 * @param contents contents to set in field
2162 */
2163 @Override
2164 public void setField(String field, String contents) throws RemoteException {
2165
2166 waitForReady();
2167
2168 final NativeDaemonEvent event;
2169 try {
2170 event = mConnector.execute("cryptfs", "setfield", field, contents);
2171 } catch (NativeDaemonConnectorException e) {
2172 throw e.rethrowAsParcelableException();
2173 }
2174 }
2175
2176 /**
2177 * Gets a field from the crypto header.
2178 * @param field field to get
2179 * @return contents of field
2180 */
2181 @Override
2182 public String getField(String field) throws RemoteException {
2183
2184 waitForReady();
2185
2186 final NativeDaemonEvent event;
2187 try {
2188 final String[] contents = NativeDaemonEvent.filterMessageList(
2189 mConnector.executeForList("cryptfs", "getfield", field),
2190 VoldResponseCode.CryptfsGetfieldResult);
2191 String result = new String();
2192 for (String content : contents) {
2193 result += content;
2194 }
2195 return result;
2196 } catch (NativeDaemonConnectorException e) {
2197 throw e.rethrowAsParcelableException();
2198 }
2199 }
2200
Jeff Sharkeyb049e212012-09-07 23:16:01 -07002201 @Override
Paul Lawrence945490c2014-03-27 16:37:28 +00002202 public String getPassword() throws RemoteException {
Rubin Xucd7a0142015-04-17 23:45:27 +01002203 mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE,
2204 "only keyguard can retrieve password");
Paul Lawrence945490c2014-03-27 16:37:28 +00002205 if (!isReady()) {
2206 return new String();
2207 }
2208
2209 final NativeDaemonEvent event;
2210 try {
2211 event = mConnector.execute("cryptfs", "getpw");
Paul Lawrence24063b52015-01-06 13:11:23 -08002212 if ("-1".equals(event.getMessage())) {
2213 // -1 equals no password
2214 return null;
2215 }
Paul Lawrence945490c2014-03-27 16:37:28 +00002216 return fromHex(event.getMessage());
2217 } catch (NativeDaemonConnectorException e) {
2218 throw e.rethrowAsParcelableException();
Paul Lawrence24063b52015-01-06 13:11:23 -08002219 } catch (IllegalArgumentException e) {
2220 Slog.e(TAG, "Invalid response to getPassword");
2221 return null;
Paul Lawrence945490c2014-03-27 16:37:28 +00002222 }
2223 }
2224
2225 @Override
2226 public void clearPassword() throws RemoteException {
2227 if (!isReady()) {
2228 return;
2229 }
2230
2231 final NativeDaemonEvent event;
2232 try {
2233 event = mConnector.execute("cryptfs", "clearpw");
2234 } catch (NativeDaemonConnectorException e) {
2235 throw e.rethrowAsParcelableException();
2236 }
2237 }
2238
2239 @Override
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002240 public int mkdirs(String callingPkg, String appPath) {
2241 final int userId = UserHandle.getUserId(Binder.getCallingUid());
2242 final UserEnvironment userEnv = new UserEnvironment(userId);
2243
2244 // Validate that reported package name belongs to caller
2245 final AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(
2246 Context.APP_OPS_SERVICE);
2247 appOps.checkPackage(Binder.getCallingUid(), callingPkg);
2248
Jeff Sharkey48877892015-03-18 11:27:19 -07002249 File appFile = null;
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002250 try {
Jeff Sharkey48877892015-03-18 11:27:19 -07002251 appFile = new File(appPath).getCanonicalFile();
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002252 } catch (IOException e) {
2253 Slog.e(TAG, "Failed to resolve " + appPath + ": " + e);
2254 return -1;
2255 }
2256
2257 // Try translating the app path into a vold path, but require that it
2258 // belong to the calling package.
Jeff Sharkey48877892015-03-18 11:27:19 -07002259 if (FileUtils.contains(userEnv.buildExternalStorageAppDataDirs(callingPkg), appFile) ||
2260 FileUtils.contains(userEnv.buildExternalStorageAppObbDirs(callingPkg), appFile) ||
2261 FileUtils.contains(userEnv.buildExternalStorageAppMediaDirs(callingPkg), appFile)) {
2262 appPath = appFile.getAbsolutePath();
2263 if (!appPath.endsWith("/")) {
2264 appPath = appPath + "/";
2265 }
2266
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002267 try {
Jeff Sharkey48877892015-03-18 11:27:19 -07002268 mConnector.execute("volume", "mkdirs", appPath);
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002269 return 0;
2270 } catch (NativeDaemonConnectorException e) {
2271 return e.getCode();
2272 }
2273 }
2274
Jeff Sharkey48877892015-03-18 11:27:19 -07002275 throw new SecurityException("Invalid mkdirs path: " + appFile);
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002276 }
2277
2278 @Override
Jeff Sharkey48877892015-03-18 11:27:19 -07002279 public StorageVolume[] getVolumeList(int userId) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002280 final ArrayList<StorageVolume> res = new ArrayList<>();
Jeff Sharkey48877892015-03-18 11:27:19 -07002281 boolean foundPrimary = false;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002282
Jeff Sharkey48877892015-03-18 11:27:19 -07002283 synchronized (mLock) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002284 for (int i = 0; i < mVolumes.size(); i++) {
2285 final VolumeInfo vol = mVolumes.valueAt(i);
Jeff Sharkey48877892015-03-18 11:27:19 -07002286 if (vol.isVisibleToUser(userId)) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002287 final StorageVolume userVol = vol.buildStorageVolume(mContext, userId);
Jeff Sharkey48877892015-03-18 11:27:19 -07002288 if (vol.isPrimary()) {
2289 res.add(0, userVol);
2290 foundPrimary = true;
2291 } else {
2292 res.add(userVol);
2293 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -07002294 }
Mike Lockwood8fa5f802011-03-24 08:12:30 -07002295 }
Mike Lockwood8fa5f802011-03-24 08:12:30 -07002296 }
Jeff Sharkey48877892015-03-18 11:27:19 -07002297
2298 if (!foundPrimary) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002299 Log.w(TAG, "No primary storage defined yet; hacking together a stub");
Jeff Sharkey48877892015-03-18 11:27:19 -07002300
2301 final boolean primaryPhysical = SystemProperties.getBoolean(
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002302 StorageManager.PROP_PRIMARY_PHYSICAL, false);
Jeff Sharkey48877892015-03-18 11:27:19 -07002303
2304 final String id = "stub_primary";
2305 final File path = Environment.getLegacyExternalStorageDirectory();
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002306 final String description = mContext.getString(android.R.string.unknownName);
Jeff Sharkey48877892015-03-18 11:27:19 -07002307 final boolean primary = true;
2308 final boolean removable = primaryPhysical;
2309 final boolean emulated = !primaryPhysical;
2310 final long mtpReserveSize = 0L;
2311 final boolean allowMassStorage = false;
2312 final long maxFileSize = 0L;
2313 final UserHandle owner = new UserHandle(userId);
2314 final String uuid = null;
Jeff Sharkey48877892015-03-18 11:27:19 -07002315 final String state = Environment.MEDIA_REMOVED;
2316
2317 res.add(0, new StorageVolume(id, MtpStorage.getStorageIdForIndex(0), path,
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002318 description, primary, removable, emulated, mtpReserveSize,
2319 allowMassStorage, maxFileSize, owner, uuid, state));
Jeff Sharkey48877892015-03-18 11:27:19 -07002320 }
2321
2322 return res.toArray(new StorageVolume[res.size()]);
Mike Lockwood8fa5f802011-03-24 08:12:30 -07002323 }
2324
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002325 @Override
2326 public DiskInfo[] getDisks() {
2327 synchronized (mLock) {
2328 final DiskInfo[] res = new DiskInfo[mDisks.size()];
2329 for (int i = 0; i < mDisks.size(); i++) {
2330 res[i] = mDisks.valueAt(i);
2331 }
2332 return res;
2333 }
2334 }
2335
2336 @Override
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07002337 public VolumeInfo[] getVolumes(int flags) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002338 synchronized (mLock) {
2339 final VolumeInfo[] res = new VolumeInfo[mVolumes.size()];
2340 for (int i = 0; i < mVolumes.size(); i++) {
2341 res[i] = mVolumes.valueAt(i);
2342 }
2343 return res;
2344 }
2345 }
2346
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07002347 @Override
2348 public VolumeRecord[] getVolumeRecords(int flags) {
2349 synchronized (mLock) {
2350 final VolumeRecord[] res = new VolumeRecord[mRecords.size()];
2351 for (int i = 0; i < mRecords.size(); i++) {
2352 res[i] = mRecords.valueAt(i);
2353 }
2354 return res;
2355 }
2356 }
2357
Kenny Rootaf9d6672010-10-08 09:21:39 -07002358 private void addObbStateLocked(ObbState obbState) throws RemoteException {
2359 final IBinder binder = obbState.getBinder();
2360 List<ObbState> obbStates = mObbMounts.get(binder);
Kenny Root5919ac62010-10-05 09:49:40 -07002361
Kenny Rootaf9d6672010-10-08 09:21:39 -07002362 if (obbStates == null) {
2363 obbStates = new ArrayList<ObbState>();
2364 mObbMounts.put(binder, obbStates);
2365 } else {
2366 for (final ObbState o : obbStates) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002367 if (o.rawPath.equals(obbState.rawPath)) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002368 throw new IllegalStateException("Attempt to add ObbState twice. "
2369 + "This indicates an error in the MountService logic.");
Kenny Root5919ac62010-10-05 09:49:40 -07002370 }
2371 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002372 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07002373
2374 obbStates.add(obbState);
2375 try {
2376 obbState.link();
2377 } catch (RemoteException e) {
2378 /*
2379 * The binder died before we could link it, so clean up our state
2380 * and return failure.
2381 */
2382 obbStates.remove(obbState);
2383 if (obbStates.isEmpty()) {
2384 mObbMounts.remove(binder);
2385 }
2386
2387 // Rethrow the error so mountObb can get it
2388 throw e;
2389 }
2390
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002391 mObbPathToStateMap.put(obbState.rawPath, obbState);
Kenny Roota02b8b02010-08-05 16:14:17 -07002392 }
2393
Kenny Rootaf9d6672010-10-08 09:21:39 -07002394 private void removeObbStateLocked(ObbState obbState) {
2395 final IBinder binder = obbState.getBinder();
2396 final List<ObbState> obbStates = mObbMounts.get(binder);
2397 if (obbStates != null) {
2398 if (obbStates.remove(obbState)) {
2399 obbState.unlink();
Kenny Root05105f72010-09-22 17:29:43 -07002400 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07002401 if (obbStates.isEmpty()) {
2402 mObbMounts.remove(binder);
2403 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002404 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002405
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002406 mObbPathToStateMap.remove(obbState.rawPath);
Kenny Root38cf8862010-09-26 14:18:51 -07002407 }
2408
Kenny Roota02b8b02010-08-05 16:14:17 -07002409 private class ObbActionHandler extends Handler {
2410 private boolean mBound = false;
Kenny Root480afe72010-10-07 10:17:50 -07002411 private final List<ObbAction> mActions = new LinkedList<ObbAction>();
Kenny Roota02b8b02010-08-05 16:14:17 -07002412
2413 ObbActionHandler(Looper l) {
2414 super(l);
2415 }
2416
2417 @Override
2418 public void handleMessage(Message msg) {
2419 switch (msg.what) {
2420 case OBB_RUN_ACTION: {
Kenny Root480afe72010-10-07 10:17:50 -07002421 final ObbAction action = (ObbAction) msg.obj;
Kenny Roota02b8b02010-08-05 16:14:17 -07002422
2423 if (DEBUG_OBB)
2424 Slog.i(TAG, "OBB_RUN_ACTION: " + action.toString());
2425
2426 // If a bind was already initiated we don't really
2427 // need to do anything. The pending install
2428 // will be processed later on.
2429 if (!mBound) {
2430 // If this is the only one pending we might
2431 // have to bind to the service again.
2432 if (!connectToService()) {
2433 Slog.e(TAG, "Failed to bind to media container service");
2434 action.handleError();
2435 return;
Kenny Roota02b8b02010-08-05 16:14:17 -07002436 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002437 }
Kenny Root735de3b2010-09-30 14:11:39 -07002438
Kenny Root735de3b2010-09-30 14:11:39 -07002439 mActions.add(action);
Kenny Roota02b8b02010-08-05 16:14:17 -07002440 break;
2441 }
2442 case OBB_MCS_BOUND: {
2443 if (DEBUG_OBB)
2444 Slog.i(TAG, "OBB_MCS_BOUND");
2445 if (msg.obj != null) {
2446 mContainerService = (IMediaContainerService) msg.obj;
2447 }
2448 if (mContainerService == null) {
2449 // Something seriously wrong. Bail out
2450 Slog.e(TAG, "Cannot bind to media container service");
2451 for (ObbAction action : mActions) {
2452 // Indicate service bind error
2453 action.handleError();
2454 }
2455 mActions.clear();
2456 } else if (mActions.size() > 0) {
Kenny Root480afe72010-10-07 10:17:50 -07002457 final ObbAction action = mActions.get(0);
Kenny Roota02b8b02010-08-05 16:14:17 -07002458 if (action != null) {
2459 action.execute(this);
2460 }
2461 } else {
2462 // Should never happen ideally.
2463 Slog.w(TAG, "Empty queue");
2464 }
2465 break;
2466 }
2467 case OBB_MCS_RECONNECT: {
2468 if (DEBUG_OBB)
2469 Slog.i(TAG, "OBB_MCS_RECONNECT");
2470 if (mActions.size() > 0) {
2471 if (mBound) {
2472 disconnectService();
2473 }
2474 if (!connectToService()) {
2475 Slog.e(TAG, "Failed to bind to media container service");
2476 for (ObbAction action : mActions) {
2477 // Indicate service bind error
2478 action.handleError();
2479 }
2480 mActions.clear();
2481 }
2482 }
2483 break;
2484 }
2485 case OBB_MCS_UNBIND: {
2486 if (DEBUG_OBB)
2487 Slog.i(TAG, "OBB_MCS_UNBIND");
2488
2489 // Delete pending install
2490 if (mActions.size() > 0) {
2491 mActions.remove(0);
2492 }
2493 if (mActions.size() == 0) {
2494 if (mBound) {
2495 disconnectService();
2496 }
2497 } else {
2498 // There are more pending requests in queue.
2499 // Just post MCS_BOUND message to trigger processing
2500 // of next pending install.
2501 mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND);
2502 }
2503 break;
2504 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07002505 case OBB_FLUSH_MOUNT_STATE: {
2506 final String path = (String) msg.obj;
2507
2508 if (DEBUG_OBB)
2509 Slog.i(TAG, "Flushing all OBB state for path " + path);
2510
2511 synchronized (mObbMounts) {
2512 final List<ObbState> obbStatesToRemove = new LinkedList<ObbState>();
2513
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002514 final Iterator<ObbState> i = mObbPathToStateMap.values().iterator();
Kenny Rootaf9d6672010-10-08 09:21:39 -07002515 while (i.hasNext()) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002516 final ObbState state = i.next();
Kenny Rootaf9d6672010-10-08 09:21:39 -07002517
2518 /*
2519 * If this entry's source file is in the volume path
2520 * that got unmounted, remove it because it's no
2521 * longer valid.
2522 */
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002523 if (state.canonicalPath.startsWith(path)) {
2524 obbStatesToRemove.add(state);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002525 }
2526 }
2527
2528 for (final ObbState obbState : obbStatesToRemove) {
2529 if (DEBUG_OBB)
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002530 Slog.i(TAG, "Removing state for " + obbState.rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002531
2532 removeObbStateLocked(obbState);
2533
2534 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002535 obbState.token.onObbResult(obbState.rawPath, obbState.nonce,
Kenny Rootaf9d6672010-10-08 09:21:39 -07002536 OnObbStateChangeListener.UNMOUNTED);
2537 } catch (RemoteException e) {
2538 Slog.i(TAG, "Couldn't send unmount notification for OBB: "
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002539 + obbState.rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002540 }
2541 }
2542 }
2543 break;
2544 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002545 }
2546 }
2547
2548 private boolean connectToService() {
2549 if (DEBUG_OBB)
2550 Slog.i(TAG, "Trying to bind to DefaultContainerService");
2551
2552 Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
2553 if (mContext.bindService(service, mDefContainerConn, Context.BIND_AUTO_CREATE)) {
2554 mBound = true;
2555 return true;
2556 }
2557 return false;
2558 }
2559
2560 private void disconnectService() {
2561 mContainerService = null;
2562 mBound = false;
2563 mContext.unbindService(mDefContainerConn);
2564 }
2565 }
2566
2567 abstract class ObbAction {
2568 private static final int MAX_RETRIES = 3;
2569 private int mRetries;
2570
2571 ObbState mObbState;
2572
2573 ObbAction(ObbState obbState) {
2574 mObbState = obbState;
2575 }
2576
2577 public void execute(ObbActionHandler handler) {
2578 try {
2579 if (DEBUG_OBB)
Ben Komalo444eca22011-09-01 15:17:44 -07002580 Slog.i(TAG, "Starting to execute action: " + toString());
Kenny Roota02b8b02010-08-05 16:14:17 -07002581 mRetries++;
2582 if (mRetries > MAX_RETRIES) {
2583 Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
Kenny Root480afe72010-10-07 10:17:50 -07002584 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
Kenny Roota02b8b02010-08-05 16:14:17 -07002585 handleError();
2586 return;
2587 } else {
2588 handleExecute();
2589 if (DEBUG_OBB)
2590 Slog.i(TAG, "Posting install MCS_UNBIND");
2591 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
2592 }
2593 } catch (RemoteException e) {
2594 if (DEBUG_OBB)
2595 Slog.i(TAG, "Posting install MCS_RECONNECT");
2596 mObbActionHandler.sendEmptyMessage(OBB_MCS_RECONNECT);
2597 } catch (Exception e) {
2598 if (DEBUG_OBB)
2599 Slog.d(TAG, "Error handling OBB action", e);
2600 handleError();
Kenny Root17eb6fb2010-10-06 15:02:52 -07002601 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
Kenny Roota02b8b02010-08-05 16:14:17 -07002602 }
2603 }
2604
Kenny Root05105f72010-09-22 17:29:43 -07002605 abstract void handleExecute() throws RemoteException, IOException;
Kenny Roota02b8b02010-08-05 16:14:17 -07002606 abstract void handleError();
Kenny Root38cf8862010-09-26 14:18:51 -07002607
2608 protected ObbInfo getObbInfo() throws IOException {
2609 ObbInfo obbInfo;
2610 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002611 obbInfo = mContainerService.getObbInfo(mObbState.ownerPath);
Kenny Root38cf8862010-09-26 14:18:51 -07002612 } catch (RemoteException e) {
2613 Slog.d(TAG, "Couldn't call DefaultContainerService to fetch OBB info for "
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002614 + mObbState.ownerPath);
Kenny Root38cf8862010-09-26 14:18:51 -07002615 obbInfo = null;
2616 }
2617 if (obbInfo == null) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002618 throw new IOException("Couldn't read OBB file: " + mObbState.ownerPath);
Kenny Root38cf8862010-09-26 14:18:51 -07002619 }
2620 return obbInfo;
2621 }
2622
Kenny Rootaf9d6672010-10-08 09:21:39 -07002623 protected void sendNewStatusOrIgnore(int status) {
2624 if (mObbState == null || mObbState.token == null) {
2625 return;
2626 }
2627
Kenny Root38cf8862010-09-26 14:18:51 -07002628 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002629 mObbState.token.onObbResult(mObbState.rawPath, mObbState.nonce, status);
Kenny Root38cf8862010-09-26 14:18:51 -07002630 } catch (RemoteException e) {
2631 Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
2632 }
2633 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002634 }
2635
2636 class MountObbAction extends ObbAction {
Ben Komalo444eca22011-09-01 15:17:44 -07002637 private final String mKey;
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002638 private final int mCallingUid;
Kenny Roota02b8b02010-08-05 16:14:17 -07002639
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002640 MountObbAction(ObbState obbState, String key, int callingUid) {
Kenny Roota02b8b02010-08-05 16:14:17 -07002641 super(obbState);
2642 mKey = key;
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002643 mCallingUid = callingUid;
Kenny Roota02b8b02010-08-05 16:14:17 -07002644 }
2645
Jason parks5af0b912010-11-29 09:05:25 -06002646 @Override
Kenny Root735de3b2010-09-30 14:11:39 -07002647 public void handleExecute() throws IOException, RemoteException {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002648 waitForReady();
2649 warnOnNotMounted();
2650
Kenny Root38cf8862010-09-26 14:18:51 -07002651 final ObbInfo obbInfo = getObbInfo();
2652
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002653 if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mCallingUid)) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002654 Slog.w(TAG, "Denied attempt to mount OBB " + obbInfo.filename
2655 + " which is owned by " + obbInfo.packageName);
2656 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
2657 return;
Kenny Roota02b8b02010-08-05 16:14:17 -07002658 }
2659
Kenny Rootaf9d6672010-10-08 09:21:39 -07002660 final boolean isMounted;
2661 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002662 isMounted = mObbPathToStateMap.containsKey(mObbState.rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002663 }
2664 if (isMounted) {
2665 Slog.w(TAG, "Attempt to mount OBB which is already mounted: " + obbInfo.filename);
2666 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_ALREADY_MOUNTED);
2667 return;
2668 }
2669
Kenny Rootaf9d6672010-10-08 09:21:39 -07002670 final String hashedKey;
2671 if (mKey == null) {
2672 hashedKey = "none";
2673 } else {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002674 try {
Kenny Root3b1abba2010-10-13 15:00:07 -07002675 SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
2676
2677 KeySpec ks = new PBEKeySpec(mKey.toCharArray(), obbInfo.salt,
2678 PBKDF2_HASH_ROUNDS, CRYPTO_ALGORITHM_KEY_SIZE);
2679 SecretKey key = factory.generateSecret(ks);
2680 BigInteger bi = new BigInteger(key.getEncoded());
2681 hashedKey = bi.toString(16);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002682 } catch (NoSuchAlgorithmException e) {
Kenny Root3b1abba2010-10-13 15:00:07 -07002683 Slog.e(TAG, "Could not load PBKDF2 algorithm", e);
2684 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
2685 return;
2686 } catch (InvalidKeySpecException e) {
2687 Slog.e(TAG, "Invalid key spec when loading PBKDF2 algorithm", e);
2688 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
Kenny Root38cf8862010-09-26 14:18:51 -07002689 return;
2690 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07002691 }
Kenny Root38cf8862010-09-26 14:18:51 -07002692
Kenny Rootaf9d6672010-10-08 09:21:39 -07002693 int rc = StorageResultCode.OperationSucceeded;
Kenny Rootaf9d6672010-10-08 09:21:39 -07002694 try {
Jeff Sharkey56cd6462013-06-07 15:09:15 -07002695 mConnector.execute("obb", "mount", mObbState.voldPath, new SensitiveArg(hashedKey),
2696 mObbState.ownerGid);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002697 } catch (NativeDaemonConnectorException e) {
2698 int code = e.getCode();
2699 if (code != VoldResponseCode.OpFailedStorageBusy) {
2700 rc = StorageResultCode.OperationFailedInternalError;
Kenny Roota02b8b02010-08-05 16:14:17 -07002701 }
2702 }
2703
Kenny Rootaf9d6672010-10-08 09:21:39 -07002704 if (rc == StorageResultCode.OperationSucceeded) {
2705 if (DEBUG_OBB)
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002706 Slog.d(TAG, "Successfully mounted OBB " + mObbState.voldPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002707
2708 synchronized (mObbMounts) {
2709 addObbStateLocked(mObbState);
2710 }
2711
2712 sendNewStatusOrIgnore(OnObbStateChangeListener.MOUNTED);
Kenny Root02c87302010-07-01 08:10:18 -07002713 } else {
Kenny Root05105f72010-09-22 17:29:43 -07002714 Slog.e(TAG, "Couldn't mount OBB file: " + rc);
Kenny Roota02b8b02010-08-05 16:14:17 -07002715
Kenny Rootaf9d6672010-10-08 09:21:39 -07002716 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT);
Kenny Root02c87302010-07-01 08:10:18 -07002717 }
2718 }
2719
Jason parks5af0b912010-11-29 09:05:25 -06002720 @Override
Kenny Roota02b8b02010-08-05 16:14:17 -07002721 public void handleError() {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002722 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
Kenny Root02c87302010-07-01 08:10:18 -07002723 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002724
2725 @Override
2726 public String toString() {
2727 StringBuilder sb = new StringBuilder();
2728 sb.append("MountObbAction{");
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002729 sb.append(mObbState);
Kenny Roota02b8b02010-08-05 16:14:17 -07002730 sb.append('}');
2731 return sb.toString();
2732 }
2733 }
2734
2735 class UnmountObbAction extends ObbAction {
Ben Komalo444eca22011-09-01 15:17:44 -07002736 private final boolean mForceUnmount;
Kenny Roota02b8b02010-08-05 16:14:17 -07002737
2738 UnmountObbAction(ObbState obbState, boolean force) {
2739 super(obbState);
2740 mForceUnmount = force;
2741 }
2742
Jason parks5af0b912010-11-29 09:05:25 -06002743 @Override
Kenny Root38cf8862010-09-26 14:18:51 -07002744 public void handleExecute() throws IOException {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002745 waitForReady();
2746 warnOnNotMounted();
2747
Kenny Root38cf8862010-09-26 14:18:51 -07002748 final ObbInfo obbInfo = getObbInfo();
Kenny Roota02b8b02010-08-05 16:14:17 -07002749
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002750 final ObbState existingState;
Kenny Root38cf8862010-09-26 14:18:51 -07002751 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002752 existingState = mObbPathToStateMap.get(mObbState.rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002753 }
Kenny Root38cf8862010-09-26 14:18:51 -07002754
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002755 if (existingState == null) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002756 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_NOT_MOUNTED);
2757 return;
2758 }
2759
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002760 if (existingState.ownerGid != mObbState.ownerGid) {
2761 Slog.w(TAG, "Permission denied attempting to unmount OBB " + existingState.rawPath
2762 + " (owned by GID " + existingState.ownerGid + ")");
Kenny Rootaf9d6672010-10-08 09:21:39 -07002763 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
2764 return;
2765 }
2766
Kenny Rootaf9d6672010-10-08 09:21:39 -07002767 int rc = StorageResultCode.OperationSucceeded;
Kenny Rootaf9d6672010-10-08 09:21:39 -07002768 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002769 final Command cmd = new Command("obb", "unmount", mObbState.voldPath);
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002770 if (mForceUnmount) {
2771 cmd.appendArg("force");
2772 }
2773 mConnector.execute(cmd);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002774 } catch (NativeDaemonConnectorException e) {
2775 int code = e.getCode();
2776 if (code == VoldResponseCode.OpFailedStorageBusy) {
2777 rc = StorageResultCode.OperationFailedStorageBusy;
2778 } else if (code == VoldResponseCode.OpFailedStorageNotFound) {
2779 // If it's not mounted then we've already won.
2780 rc = StorageResultCode.OperationSucceeded;
2781 } else {
2782 rc = StorageResultCode.OperationFailedInternalError;
Kenny Roota02b8b02010-08-05 16:14:17 -07002783 }
2784 }
2785
Kenny Rootaf9d6672010-10-08 09:21:39 -07002786 if (rc == StorageResultCode.OperationSucceeded) {
2787 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002788 removeObbStateLocked(existingState);
Kenny Root38cf8862010-09-26 14:18:51 -07002789 }
2790
Kenny Rootaf9d6672010-10-08 09:21:39 -07002791 sendNewStatusOrIgnore(OnObbStateChangeListener.UNMOUNTED);
Kenny Roota02b8b02010-08-05 16:14:17 -07002792 } else {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002793 Slog.w(TAG, "Could not unmount OBB: " + existingState);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002794 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_UNMOUNT);
Kenny Roota02b8b02010-08-05 16:14:17 -07002795 }
2796 }
2797
Jason parks5af0b912010-11-29 09:05:25 -06002798 @Override
Kenny Roota02b8b02010-08-05 16:14:17 -07002799 public void handleError() {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002800 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
Kenny Roota02b8b02010-08-05 16:14:17 -07002801 }
2802
2803 @Override
2804 public String toString() {
2805 StringBuilder sb = new StringBuilder();
2806 sb.append("UnmountObbAction{");
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002807 sb.append(mObbState);
Kenny Roota02b8b02010-08-05 16:14:17 -07002808 sb.append(",force=");
2809 sb.append(mForceUnmount);
Kenny Roota02b8b02010-08-05 16:14:17 -07002810 sb.append('}');
2811 return sb.toString();
2812 }
Kenny Root02c87302010-07-01 08:10:18 -07002813 }
Kenny Root38cf8862010-09-26 14:18:51 -07002814
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -08002815 @VisibleForTesting
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002816 public static String buildObbPath(final String canonicalPath, int userId, boolean forVold) {
2817 // TODO: allow caller to provide Environment for full testing
Jeff Sharkey1abdb712013-08-11 16:28:14 -07002818 // TODO: extend to support OBB mounts on secondary external storage
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002819
2820 // Only adjust paths when storage is emulated
2821 if (!Environment.isExternalStorageEmulated()) {
2822 return canonicalPath;
2823 }
2824
2825 String path = canonicalPath.toString();
2826
2827 // First trim off any external storage prefix
2828 final UserEnvironment userEnv = new UserEnvironment(userId);
2829
2830 // /storage/emulated/0
Jeff Sharkey1abdb712013-08-11 16:28:14 -07002831 final String externalPath = userEnv.getExternalStorageDirectory().getAbsolutePath();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002832 // /storage/emulated_legacy
2833 final String legacyExternalPath = Environment.getLegacyExternalStorageDirectory()
Jeff Sharkey1abdb712013-08-11 16:28:14 -07002834 .getAbsolutePath();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002835
2836 if (path.startsWith(externalPath)) {
2837 path = path.substring(externalPath.length() + 1);
2838 } else if (path.startsWith(legacyExternalPath)) {
2839 path = path.substring(legacyExternalPath.length() + 1);
2840 } else {
2841 return canonicalPath;
2842 }
2843
2844 // Handle special OBB paths on emulated storage
2845 final String obbPath = "Android/obb";
2846 if (path.startsWith(obbPath)) {
2847 path = path.substring(obbPath.length() + 1);
2848
Jeff Sharkey48877892015-03-18 11:27:19 -07002849 final UserEnvironment ownerEnv = new UserEnvironment(UserHandle.USER_OWNER);
2850 return new File(ownerEnv.buildExternalStorageAndroidObbDirs()[0], path)
2851 .getAbsolutePath();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002852 }
2853
2854 // Handle normal external storage paths
Jeff Sharkey48877892015-03-18 11:27:19 -07002855 return new File(userEnv.getExternalStorageDirectory(), path).getAbsolutePath();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002856 }
2857
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07002858 private static class Callbacks extends Handler {
2859 private static final int MSG_STORAGE_STATE_CHANGED = 1;
2860 private static final int MSG_VOLUME_STATE_CHANGED = 2;
Jeff Sharkey50a05452015-04-29 11:24:52 -07002861 private static final int MSG_VOLUME_RECORD_CHANGED = 3;
2862 private static final int MSG_VOLUME_FORGOTTEN = 4;
2863 private static final int MSG_DISK_SCANNED = 5;
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07002864
2865 private final RemoteCallbackList<IMountServiceListener>
2866 mCallbacks = new RemoteCallbackList<>();
2867
2868 public Callbacks(Looper looper) {
2869 super(looper);
2870 }
2871
2872 public void register(IMountServiceListener callback) {
2873 mCallbacks.register(callback);
2874 }
2875
2876 public void unregister(IMountServiceListener callback) {
2877 mCallbacks.unregister(callback);
2878 }
2879
2880 @Override
2881 public void handleMessage(Message msg) {
2882 final SomeArgs args = (SomeArgs) msg.obj;
2883 final int n = mCallbacks.beginBroadcast();
2884 for (int i = 0; i < n; i++) {
2885 final IMountServiceListener callback = mCallbacks.getBroadcastItem(i);
2886 try {
2887 invokeCallback(callback, msg.what, args);
2888 } catch (RemoteException ignored) {
2889 }
2890 }
2891 mCallbacks.finishBroadcast();
2892 args.recycle();
2893 }
2894
2895 private void invokeCallback(IMountServiceListener callback, int what, SomeArgs args)
2896 throws RemoteException {
2897 switch (what) {
2898 case MSG_STORAGE_STATE_CHANGED: {
2899 callback.onStorageStateChanged((String) args.arg1, (String) args.arg2,
2900 (String) args.arg3);
2901 break;
2902 }
2903 case MSG_VOLUME_STATE_CHANGED: {
2904 callback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3);
2905 break;
2906 }
Jeff Sharkey50a05452015-04-29 11:24:52 -07002907 case MSG_VOLUME_RECORD_CHANGED: {
2908 callback.onVolumeRecordChanged((VolumeRecord) args.arg1);
2909 break;
2910 }
2911 case MSG_VOLUME_FORGOTTEN: {
2912 callback.onVolumeForgotten((String) args.arg1);
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07002913 break;
2914 }
Jeff Sharkey620b32b2015-04-23 19:36:02 -07002915 case MSG_DISK_SCANNED: {
2916 callback.onDiskScanned((DiskInfo) args.arg1, args.argi2);
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07002917 break;
2918 }
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07002919 }
2920 }
2921
2922 private void notifyStorageStateChanged(String path, String oldState, String newState) {
2923 final SomeArgs args = SomeArgs.obtain();
2924 args.arg1 = path;
2925 args.arg2 = oldState;
2926 args.arg3 = newState;
2927 obtainMessage(MSG_STORAGE_STATE_CHANGED, args).sendToTarget();
2928 }
2929
2930 private void notifyVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
2931 final SomeArgs args = SomeArgs.obtain();
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07002932 args.arg1 = vol.clone();
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07002933 args.argi2 = oldState;
2934 args.argi3 = newState;
2935 obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget();
2936 }
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07002937
Jeff Sharkey50a05452015-04-29 11:24:52 -07002938 private void notifyVolumeRecordChanged(VolumeRecord rec) {
2939 final SomeArgs args = SomeArgs.obtain();
2940 args.arg1 = rec.clone();
2941 obtainMessage(MSG_VOLUME_RECORD_CHANGED, args).sendToTarget();
2942 }
2943
2944 private void notifyVolumeForgotten(String fsUuid) {
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07002945 final SomeArgs args = SomeArgs.obtain();
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07002946 args.arg1 = fsUuid;
Jeff Sharkey50a05452015-04-29 11:24:52 -07002947 obtainMessage(MSG_VOLUME_FORGOTTEN, args).sendToTarget();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07002948 }
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07002949
Jeff Sharkey620b32b2015-04-23 19:36:02 -07002950 private void notifyDiskScanned(DiskInfo disk, int volumeCount) {
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07002951 final SomeArgs args = SomeArgs.obtain();
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07002952 args.arg1 = disk.clone();
Jeff Sharkey620b32b2015-04-23 19:36:02 -07002953 args.argi2 = volumeCount;
2954 obtainMessage(MSG_DISK_SCANNED, args).sendToTarget();
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07002955 }
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07002956 }
2957
Kenny Root38cf8862010-09-26 14:18:51 -07002958 @Override
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002959 protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
2960 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
2961
Jeff Sharkey27de30d2015-04-18 16:20:27 -07002962 for (String arg : args) {
Jeff Sharkey50a05452015-04-29 11:24:52 -07002963 if ("--forget-all".equals(arg)) {
2964 forgetAll();
Jeff Sharkey27de30d2015-04-18 16:20:27 -07002965 }
2966 }
2967
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002968 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ", 160);
Jeff Sharkey27de30d2015-04-18 16:20:27 -07002969 synchronized (mLock) {
2970 pw.println("Disks:");
2971 pw.increaseIndent();
2972 for (int i = 0; i < mDisks.size(); i++) {
2973 final DiskInfo disk = mDisks.valueAt(i);
2974 disk.dump(pw);
2975 }
2976 pw.decreaseIndent();
2977
2978 pw.println();
2979 pw.println("Volumes:");
2980 pw.increaseIndent();
2981 for (int i = 0; i < mVolumes.size(); i++) {
2982 final VolumeInfo vol = mVolumes.valueAt(i);
2983 if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.id)) continue;
2984 vol.dump(pw);
2985 }
2986 pw.decreaseIndent();
2987
2988 pw.println();
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07002989 pw.println("Records:");
Jeff Sharkey27de30d2015-04-18 16:20:27 -07002990 pw.increaseIndent();
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07002991 for (int i = 0; i < mRecords.size(); i++) {
2992 final VolumeRecord note = mRecords.valueAt(i);
2993 note.dump(pw);
Jeff Sharkey27de30d2015-04-18 16:20:27 -07002994 }
2995 pw.decreaseIndent();
Jeff Sharkey275e3e42015-04-24 16:10:32 -07002996
2997 pw.println();
2998 pw.println("Primary storage UUID: " + mPrimaryStorageUuid);
Jeff Sharkey27de30d2015-04-18 16:20:27 -07002999 }
Kenny Root38cf8862010-09-26 14:18:51 -07003000
Kenny Root38cf8862010-09-26 14:18:51 -07003001 synchronized (mObbMounts) {
Jeff Sharkey27de30d2015-04-18 16:20:27 -07003002 pw.println();
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003003 pw.println("mObbMounts:");
3004 pw.increaseIndent();
3005 final Iterator<Entry<IBinder, List<ObbState>>> binders = mObbMounts.entrySet()
3006 .iterator();
Kenny Rootaf9d6672010-10-08 09:21:39 -07003007 while (binders.hasNext()) {
3008 Entry<IBinder, List<ObbState>> e = binders.next();
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003009 pw.println(e.getKey() + ":");
3010 pw.increaseIndent();
Kenny Rootaf9d6672010-10-08 09:21:39 -07003011 final List<ObbState> obbStates = e.getValue();
Kenny Root38cf8862010-09-26 14:18:51 -07003012 for (final ObbState obbState : obbStates) {
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003013 pw.println(obbState);
Kenny Root38cf8862010-09-26 14:18:51 -07003014 }
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003015 pw.decreaseIndent();
Kenny Root38cf8862010-09-26 14:18:51 -07003016 }
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003017 pw.decreaseIndent();
Kenny Rootaf9d6672010-10-08 09:21:39 -07003018
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003019 pw.println();
3020 pw.println("mObbPathToStateMap:");
3021 pw.increaseIndent();
Kenny Rootaf9d6672010-10-08 09:21:39 -07003022 final Iterator<Entry<String, ObbState>> maps = mObbPathToStateMap.entrySet().iterator();
3023 while (maps.hasNext()) {
3024 final Entry<String, ObbState> e = maps.next();
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003025 pw.print(e.getKey());
3026 pw.print(" -> ");
3027 pw.println(e.getValue());
Kenny Rootaf9d6672010-10-08 09:21:39 -07003028 }
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003029 pw.decreaseIndent();
Kenny Root38cf8862010-09-26 14:18:51 -07003030 }
Kenny Root4161f9b2011-07-13 09:48:33 -07003031
Robert Greenwalt470fd722012-01-18 12:51:15 -08003032 pw.println();
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003033 pw.println("mConnection:");
3034 pw.increaseIndent();
Robert Greenwalt470fd722012-01-18 12:51:15 -08003035 mConnector.dump(fd, pw, args);
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003036 pw.decreaseIndent();
Christopher Tate7265abe2014-11-21 13:54:45 -08003037
3038 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
3039
3040 pw.println();
3041 pw.print("Last maintenance: ");
3042 pw.println(sdf.format(new Date(mLastMaintenance)));
Kenny Root38cf8862010-09-26 14:18:51 -07003043 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003044
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -07003045 /** {@inheritDoc} */
Jeff Sharkey48877892015-03-18 11:27:19 -07003046 @Override
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -07003047 public void monitor() {
3048 if (mConnector != null) {
3049 mConnector.monitor();
3050 }
3051 }
3052}