blob: d48953db8f45e74dd54f17540b59a42d062e1e18 [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 Sharkey4c099d02015-05-15 13:45:00 -070019import static com.android.internal.util.XmlUtils.readBooleanAttribute;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070020import static com.android.internal.util.XmlUtils.readIntAttribute;
21import static com.android.internal.util.XmlUtils.readStringAttribute;
Jeff Sharkey4c099d02015-05-15 13:45:00 -070022import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070023import static com.android.internal.util.XmlUtils.writeIntAttribute;
24import static com.android.internal.util.XmlUtils.writeStringAttribute;
25import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
26import static org.xmlpull.v1.XmlPullParser.START_TAG;
27
Jason parks8888c592011-01-20 22:46:41 -060028import android.Manifest;
Elliott Hughesf839b4f2014-09-26 12:30:47 -070029import android.app.ActivityManagerNative;
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -070030import android.app.AppOpsManager;
Kenny Roota02b8b02010-08-05 16:14:17 -070031import android.content.ComponentName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032import android.content.Context;
33import android.content.Intent;
Kenny Roota02b8b02010-08-05 16:14:17 -070034import android.content.ServiceConnection;
Jeff Sharkey275e3e42015-04-24 16:10:32 -070035import android.content.pm.IPackageMoveObserver;
36import android.content.pm.PackageManager;
Elliott Hughesf839b4f2014-09-26 12:30:47 -070037import android.content.res.Configuration;
Kenny Root02c87302010-07-01 08:10:18 -070038import android.content.res.ObbInfo;
Jeff Sharkey48877892015-03-18 11:27:19 -070039import android.mtp.MtpStorage;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.net.Uri;
Kenny Root02c87302010-07-01 08:10:18 -070041import android.os.Binder;
Jeff Sharkey4c099d02015-05-15 13:45:00 -070042import android.os.DropBoxManager;
Kenny Roota02b8b02010-08-05 16:14:17 -070043import android.os.Environment;
Jeff Sharkeyb049e212012-09-07 23:16:01 -070044import android.os.Environment.UserEnvironment;
Jeff Sharkey48877892015-03-18 11:27:19 -070045import android.os.FileUtils;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080046import android.os.Handler;
Dianne Hackbornefa92b22013-05-03 14:11:43 -070047import android.os.HandlerThread;
Kenny Roota02b8b02010-08-05 16:14:17 -070048import android.os.IBinder;
Daniel Sandler5f27ef42010-03-16 15:42:02 -040049import android.os.Looper;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080050import android.os.Message;
Jeff Sharkey7151a9a2015-04-04 15:22:37 -070051import android.os.RemoteCallbackList;
San Mehat4270e1e2010-01-29 05:32:19 -080052import android.os.RemoteException;
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -080053import android.os.ServiceManager;
Svetoslavf23b64d2013-04-25 14:45:54 -070054import android.os.SystemClock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055import android.os.SystemProperties;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070056import android.os.UserHandle;
Emily Bernier92aa5a22014-07-07 10:11:48 -040057import android.os.UserManager;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070058import android.os.storage.DiskInfo;
Kenny Roota02b8b02010-08-05 16:14:17 -070059import android.os.storage.IMountService;
60import android.os.storage.IMountServiceListener;
61import android.os.storage.IMountShutdownObserver;
62import android.os.storage.IObbActionListener;
Kenny Rootaf9d6672010-10-08 09:21:39 -070063import android.os.storage.OnObbStateChangeListener;
Paul Lawrence46791e72014-04-03 09:10:26 -070064import android.os.storage.StorageManager;
Kenny Roota02b8b02010-08-05 16:14:17 -070065import android.os.storage.StorageResultCode;
Mike Lockwood2f6a3882011-05-09 19:08:06 -070066import android.os.storage.StorageVolume;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070067import android.os.storage.VolumeInfo;
Jeff Sharkeyb36586a2015-04-27 08:42:28 -070068import android.os.storage.VolumeRecord;
Jason parksf7b3cd42011-01-27 09:28:25 -060069import android.text.TextUtils;
Jeff Sharkey1783f142015-04-17 10:52:51 -070070import android.text.format.DateUtils;
Jeff Sharkey48877892015-03-18 11:27:19 -070071import android.util.ArrayMap;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070072import android.util.AtomicFile;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070073import android.util.Log;
San Mehata5078592010-03-25 09:36:54 -070074import android.util.Slog;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070075import android.util.Xml;
Jeff Sharkey48877892015-03-18 11:27:19 -070076
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070077import libcore.io.IoUtils;
Jeff Sharkey48877892015-03-18 11:27:19 -070078import libcore.util.EmptyArray;
79import libcore.util.HexEncoding;
Mike Lockwood2f6a3882011-05-09 19:08:06 -070080
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -080081import com.android.internal.annotations.GuardedBy;
82import com.android.internal.annotations.VisibleForTesting;
Jeff Sharkeyb049e212012-09-07 23:16:01 -070083import com.android.internal.app.IMediaContainerService;
Jeff Sharkey7151a9a2015-04-04 15:22:37 -070084import com.android.internal.os.SomeArgs;
Jeff Sharkey48877892015-03-18 11:27:19 -070085import com.android.internal.util.ArrayUtils;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070086import com.android.internal.util.FastXmlSerializer;
Jeff Sharkey5aca2b82013-10-16 16:21:54 -070087import com.android.internal.util.IndentingPrintWriter;
Jeff Sharkey4fbbda42012-09-24 18:34:07 -070088import com.android.internal.util.Preconditions;
Jeff Sharkeyb049e212012-09-07 23:16:01 -070089import com.android.server.NativeDaemonConnector.Command;
Jeff Sharkey56cd6462013-06-07 15:09:15 -070090import com.android.server.NativeDaemonConnector.SensitiveArg;
Jeff Sharkeyb049e212012-09-07 23:16:01 -070091import com.android.server.pm.PackageManagerService;
Kenny Roota02b8b02010-08-05 16:14:17 -070092
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070093import org.xmlpull.v1.XmlPullParser;
94import org.xmlpull.v1.XmlPullParserException;
95import org.xmlpull.v1.XmlSerializer;
96
Jeff Sharkeyb049e212012-09-07 23:16:01 -070097import java.io.File;
Kenny Root38cf8862010-09-26 14:18:51 -070098import java.io.FileDescriptor;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070099import java.io.FileInputStream;
100import java.io.FileNotFoundException;
Christopher Tate7265abe2014-11-21 13:54:45 -0800101import java.io.FileOutputStream;
Kenny Root05105f72010-09-22 17:29:43 -0700102import java.io.IOException;
Kenny Root38cf8862010-09-26 14:18:51 -0700103import java.io.PrintWriter;
Kenny Root3b1abba2010-10-13 15:00:07 -0700104import java.math.BigInteger;
Paul Lawrence8e397362014-01-27 15:22:30 -0800105import java.nio.charset.StandardCharsets;
Kenny Root735de3b2010-09-30 14:11:39 -0700106import java.security.NoSuchAlgorithmException;
Kenny Root3b1abba2010-10-13 15:00:07 -0700107import java.security.spec.InvalidKeySpecException;
108import java.security.spec.KeySpec;
Christopher Tate7265abe2014-11-21 13:54:45 -0800109import java.text.SimpleDateFormat;
San Mehat22dd86e2010-01-12 12:21:18 -0800110import java.util.ArrayList;
Christopher Tate7265abe2014-11-21 13:54:45 -0800111import java.util.Date;
Kenny Roota02b8b02010-08-05 16:14:17 -0700112import java.util.HashMap;
San Mehat6cdd9c02010-02-09 14:45:20 -0800113import java.util.HashSet;
Kenny Root38cf8862010-09-26 14:18:51 -0700114import java.util.Iterator;
Kenny Roota02b8b02010-08-05 16:14:17 -0700115import java.util.LinkedList;
116import java.util.List;
Elliott Hughesf839b4f2014-09-26 12:30:47 -0700117import java.util.Locale;
Kenny Roota02b8b02010-08-05 16:14:17 -0700118import java.util.Map;
Kenny Root38cf8862010-09-26 14:18:51 -0700119import java.util.Map.Entry;
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700120import java.util.Objects;
Kenny Root51a573c2012-05-17 13:30:28 -0700121import java.util.concurrent.CountDownLatch;
122import java.util.concurrent.TimeUnit;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800123
Kenny Root3b1abba2010-10-13 15:00:07 -0700124import javax.crypto.SecretKey;
125import javax.crypto.SecretKeyFactory;
126import javax.crypto.spec.PBEKeySpec;
127
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800128/**
Jeff Sharkey48877892015-03-18 11:27:19 -0700129 * Service responsible for various storage media. Connects to {@code vold} to
130 * watch for and manage dynamically added storage, such as SD cards and USB mass
131 * storage. Also decides how storage should be presented to users on the device.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800132 */
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -0700133class MountService extends IMountService.Stub
134 implements INativeDaemonConnectorCallbacks, Watchdog.Monitor {
Jason parks5af0b912010-11-29 09:05:25 -0600135
Jeff Sharkey48877892015-03-18 11:27:19 -0700136 // TODO: finish enforcing UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA
137
Christopher Tated417d622013-08-19 16:14:25 -0700138 // Static direct instance pointer for the tightly-coupled idle service to use
139 static MountService sSelf = null;
140
Jeff Sharkey56e62932015-03-21 20:41:00 -0700141 public static class Lifecycle extends SystemService {
142 private MountService mMountService;
143
144 public Lifecycle(Context context) {
145 super(context);
146 }
147
148 @Override
149 public void onStart() {
150 mMountService = new MountService(getContext());
151 publishBinderService("mount", mMountService);
152 }
153
154 @Override
155 public void onBootPhase(int phase) {
156 if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
157 mMountService.systemReady();
158 }
159 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700160
161 @Override
162 public void onStartUser(int userHandle) {
163 mMountService.onStartUser(userHandle);
164 }
165
166 @Override
167 public void onCleanupUser(int userHandle) {
168 mMountService.onCleanupUser(userHandle);
169 }
Jeff Sharkey56e62932015-03-21 20:41:00 -0700170 }
171
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800172 private static final boolean LOCAL_LOGD = false;
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800173 private static final boolean DEBUG_EVENTS = false;
Kenny Rootb7db2722011-01-25 16:39:35 -0800174 private static final boolean DEBUG_OBB = false;
Kenny Root02c87302010-07-01 08:10:18 -0700175
Kenny Root07714d42011-08-17 17:49:28 -0700176 // Disable this since it messes up long-running cryptfs operations.
177 private static final boolean WATCHDOG_ENABLE = false;
178
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179 private static final String TAG = "MountService";
Jeff Sharkey9756d752015-05-14 21:07:42 -0700180 private static final String TAG_STORAGE_BENCHMARK = "storage_benchmark";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800181
Kenny Root305bcbf2010-09-03 07:56:38 -0700182 private static final String VOLD_TAG = "VoldConnector";
183
Kenny Rootcf0b38c2011-03-22 14:17:59 -0700184 /** Maximum number of ASEC containers allowed to be mounted. */
185 private static final int MAX_CONTAINERS = 250;
186
Jeff Sharkey275e3e42015-04-24 16:10:32 -0700187 /** Magic value sent by MoveTask.cpp */
188 private static final int MOVE_STATUS_COPY_FINISHED = 82;
189
San Mehat4270e1e2010-01-29 05:32:19 -0800190 /*
191 * Internal vold response code constants
192 */
San Mehat22dd86e2010-01-12 12:21:18 -0800193 class VoldResponseCode {
San Mehat4270e1e2010-01-29 05:32:19 -0800194 /*
195 * 100 series - Requestion action was initiated; expect another reply
196 * before proceeding with a new command.
197 */
San Mehat22dd86e2010-01-12 12:21:18 -0800198 public static final int VolumeListResult = 110;
199 public static final int AsecListResult = 111;
San Mehatc1b4ce92010-02-16 17:13:03 -0800200 public static final int StorageUsersListResult = 112;
Paul Lawrencee51dcf92014-03-18 10:56:00 -0700201 public static final int CryptfsGetfieldResult = 113;
San Mehat22dd86e2010-01-12 12:21:18 -0800202
San Mehat4270e1e2010-01-29 05:32:19 -0800203 /*
204 * 200 series - Requestion action has been successfully completed.
205 */
206 public static final int ShareStatusResult = 210;
San Mehat22dd86e2010-01-12 12:21:18 -0800207 public static final int AsecPathResult = 211;
San Mehat4270e1e2010-01-29 05:32:19 -0800208 public static final int ShareEnabledResult = 212;
San Mehat22dd86e2010-01-12 12:21:18 -0800209
San Mehat4270e1e2010-01-29 05:32:19 -0800210 /*
211 * 400 series - Command was accepted, but the requested action
212 * did not take place.
213 */
214 public static final int OpFailedNoMedia = 401;
215 public static final int OpFailedMediaBlank = 402;
216 public static final int OpFailedMediaCorrupt = 403;
217 public static final int OpFailedVolNotMounted = 404;
San Mehatd9709982010-02-18 11:43:03 -0800218 public static final int OpFailedStorageBusy = 405;
San Mehat2d66cef2010-03-23 11:12:52 -0700219 public static final int OpFailedStorageNotFound = 406;
San Mehat4270e1e2010-01-29 05:32:19 -0800220
221 /*
222 * 600 series - Unsolicited broadcasts.
223 */
Jeff Sharkey48877892015-03-18 11:27:19 -0700224 public static final int DISK_CREATED = 640;
225 public static final int DISK_SIZE_CHANGED = 641;
226 public static final int DISK_LABEL_CHANGED = 642;
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700227 public static final int DISK_SCANNED = 643;
Jeff Sharkey48877892015-03-18 11:27:19 -0700228 public static final int DISK_DESTROYED = 649;
229
230 public static final int VOLUME_CREATED = 650;
231 public static final int VOLUME_STATE_CHANGED = 651;
232 public static final int VOLUME_FS_TYPE_CHANGED = 652;
233 public static final int VOLUME_FS_UUID_CHANGED = 653;
234 public static final int VOLUME_FS_LABEL_CHANGED = 654;
235 public static final int VOLUME_PATH_CHANGED = 655;
Jeff Sharkey50a05452015-04-29 11:24:52 -0700236 public static final int VOLUME_INTERNAL_PATH_CHANGED = 656;
Jeff Sharkey48877892015-03-18 11:27:19 -0700237 public static final int VOLUME_DESTROYED = 659;
Svetoslavf23b64d2013-04-25 14:45:54 -0700238
Jeff Sharkey275e3e42015-04-24 16:10:32 -0700239 public static final int MOVE_STATUS = 660;
Jeff Sharkey9756d752015-05-14 21:07:42 -0700240 public static final int BENCHMARK_RESULT = 661;
Jeff Sharkey275e3e42015-04-24 16:10:32 -0700241
Svetoslavf23b64d2013-04-25 14:45:54 -0700242 /*
243 * 700 series - fstrim
244 */
245 public static final int FstrimCompleted = 700;
San Mehat22dd86e2010-01-12 12:21:18 -0800246 }
247
Jeff Sharkey620b32b2015-04-23 19:36:02 -0700248 private static final int VERSION_INIT = 1;
249 private static final int VERSION_ADD_PRIMARY = 2;
Jeff Sharkeyfced5342015-05-10 14:53:34 -0700250 private static final int VERSION_FIX_PRIMARY = 3;
Jeff Sharkey620b32b2015-04-23 19:36:02 -0700251
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700252 private static final String TAG_VOLUMES = "volumes";
Jeff Sharkey620b32b2015-04-23 19:36:02 -0700253 private static final String ATTR_VERSION = "version";
254 private static final String ATTR_PRIMARY_STORAGE_UUID = "primaryStorageUuid";
Jeff Sharkey4c099d02015-05-15 13:45:00 -0700255 private static final String ATTR_FORCE_ADOPTABLE = "forceAdoptable";
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700256 private static final String TAG_VOLUME = "volume";
257 private static final String ATTR_TYPE = "type";
258 private static final String ATTR_FS_UUID = "fsUuid";
259 private static final String ATTR_NICKNAME = "nickname";
260 private static final String ATTR_USER_FLAGS = "userFlags";
261
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700262 private final AtomicFile mSettingsFile;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700263
Jeff Sharkey48877892015-03-18 11:27:19 -0700264 /**
265 * <em>Never</em> hold the lock while performing downcalls into vold, since
266 * unsolicited events can suddenly appear to update data structures.
267 */
268 private final Object mLock = new Object();
269
270 @GuardedBy("mLock")
271 private int[] mStartedUsers = EmptyArray.INT;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700272
273 /** Map from disk ID to disk */
Jeff Sharkey48877892015-03-18 11:27:19 -0700274 @GuardedBy("mLock")
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700275 private ArrayMap<String, DiskInfo> mDisks = new ArrayMap<>();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700276 /** Map from volume ID to disk */
Jeff Sharkey48877892015-03-18 11:27:19 -0700277 @GuardedBy("mLock")
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700278 private ArrayMap<String, VolumeInfo> mVolumes = new ArrayMap<>();
Jeff Sharkey48877892015-03-18 11:27:19 -0700279
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700280 /** Map from UUID to record */
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700281 @GuardedBy("mLock")
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700282 private ArrayMap<String, VolumeRecord> mRecords = new ArrayMap<>();
Jeff Sharkey620b32b2015-04-23 19:36:02 -0700283 @GuardedBy("mLock")
284 private String mPrimaryStorageUuid;
Jeff Sharkey4c099d02015-05-15 13:45:00 -0700285 @GuardedBy("mLock")
286 private boolean mForceAdoptable;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700287
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700288 /** Map from disk ID to latches */
289 @GuardedBy("mLock")
290 private ArrayMap<String, CountDownLatch> mDiskScanLatches = new ArrayMap<>();
291
Jeff Sharkey275e3e42015-04-24 16:10:32 -0700292 @GuardedBy("mLock")
293 private IPackageMoveObserver mMoveCallback;
294 @GuardedBy("mLock")
295 private String mMoveTargetUuid;
296
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700297 private DiskInfo findDiskById(String id) {
298 synchronized (mLock) {
299 final DiskInfo disk = mDisks.get(id);
300 if (disk != null) {
301 return disk;
302 }
303 }
304 throw new IllegalArgumentException("No disk found for ID " + id);
305 }
306
307 private VolumeInfo findVolumeById(String id) {
308 synchronized (mLock) {
309 final VolumeInfo vol = mVolumes.get(id);
310 if (vol != null) {
311 return vol;
312 }
313 }
314 throw new IllegalArgumentException("No volume found for ID " + id);
315 }
316
Jeff Sharkey48877892015-03-18 11:27:19 -0700317 @Deprecated
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700318 private String findVolumeIdForPath(String path) {
Jeff Sharkey48877892015-03-18 11:27:19 -0700319 synchronized (mLock) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700320 for (int i = 0; i < mVolumes.size(); i++) {
321 final VolumeInfo vol = mVolumes.valueAt(i);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700322 if (vol.path != null && path.startsWith(vol.path)) {
323 return vol.id;
Jeff Sharkey48877892015-03-18 11:27:19 -0700324 }
325 }
326 }
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700327 throw new IllegalArgumentException("No volume found for path " + path);
Jeff Sharkey48877892015-03-18 11:27:19 -0700328 }
329
Jeff Sharkey275e3e42015-04-24 16:10:32 -0700330 private VolumeInfo findStorageForUuid(String volumeUuid) {
331 final StorageManager storage = mContext.getSystemService(StorageManager.class);
332 if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) {
333 return findVolumeById(VolumeInfo.ID_EMULATED_INTERNAL);
334 } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
335 return storage.getPrimaryPhysicalVolume();
336 } else {
337 return storage.findEmulatedForPrivate(storage.findVolumeByUuid(volumeUuid));
338 }
339 }
340
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700341 private CountDownLatch findOrCreateDiskScanLatch(String diskId) {
342 synchronized (mLock) {
343 CountDownLatch latch = mDiskScanLatches.get(diskId);
344 if (latch == null) {
345 latch = new CountDownLatch(1);
346 mDiskScanLatches.put(diskId, latch);
347 }
348 return latch;
349 }
350 }
351
Jeff Sharkey48877892015-03-18 11:27:19 -0700352 private static int sNextMtpIndex = 1;
353
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700354 private static int allocateMtpIndex(String volId) {
355 if (VolumeInfo.ID_EMULATED_INTERNAL.equals(volId)) {
356 return 0;
357 } else {
358 return sNextMtpIndex++;
Jeff Sharkey48877892015-03-18 11:27:19 -0700359 }
360 }
361
Paul Lawrence8e397362014-01-27 15:22:30 -0800362 /** List of crypto types.
363 * These must match CRYPT_TYPE_XXX in cryptfs.h AND their
364 * corresponding commands in CommandListener.cpp */
365 public static final String[] CRYPTO_TYPES
366 = { "password", "default", "pattern", "pin" };
367
Brian Carlstrom7395a8a2014-04-28 22:11:01 -0700368 private final Context mContext;
Brian Carlstromdfad99a2014-05-07 15:21:14 -0700369 private final NativeDaemonConnector mConnector;
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700370
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700371 private volatile boolean mSystemReady = false;
Jeff Sharkey48877892015-03-18 11:27:19 -0700372 private volatile boolean mDaemonConnected = false;
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700373
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700374 private PackageManagerService mPms;
375
376 private final Callbacks mCallbacks;
Jeff Sharkey48877892015-03-18 11:27:19 -0700377
Jeff Sharkey0be607c2012-11-14 14:39:19 -0800378 private final CountDownLatch mConnectedSignal = new CountDownLatch(1);
379 private final CountDownLatch mAsecsScanned = new CountDownLatch(1);
Jeff Sharkey48877892015-03-18 11:27:19 -0700380
381 private final Object mUnmountLock = new Object();
382 @GuardedBy("mUnmountLock")
383 private CountDownLatch mUnmountSignal;
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -0800384
San Mehat6cdd9c02010-02-09 14:45:20 -0800385 /**
386 * Private hash of currently mounted secure containers.
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800387 * Used as a lock in methods to manipulate secure containers.
San Mehat6cdd9c02010-02-09 14:45:20 -0800388 */
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800389 final private HashSet<String> mAsecMountSet = new HashSet<String>();
San Mehat6cdd9c02010-02-09 14:45:20 -0800390
Kenny Root02c87302010-07-01 08:10:18 -0700391 /**
Kenny Root3b1abba2010-10-13 15:00:07 -0700392 * The size of the crypto algorithm key in bits for OBB files. Currently
393 * Twofish is used which takes 128-bit keys.
394 */
395 private static final int CRYPTO_ALGORITHM_KEY_SIZE = 128;
396
397 /**
398 * The number of times to run SHA1 in the PBKDF2 function for OBB files.
399 * 1024 is reasonably secure and not too slow.
400 */
401 private static final int PBKDF2_HASH_ROUNDS = 1024;
402
403 /**
Kenny Roota02b8b02010-08-05 16:14:17 -0700404 * Mounted OBB tracking information. Used to track the current state of all
405 * OBBs.
Kenny Root02c87302010-07-01 08:10:18 -0700406 */
Kenny Root735de3b2010-09-30 14:11:39 -0700407 final private Map<IBinder, List<ObbState>> mObbMounts = new HashMap<IBinder, List<ObbState>>();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700408
409 /** Map from raw paths to {@link ObbState}. */
Kenny Roota02b8b02010-08-05 16:14:17 -0700410 final private Map<String, ObbState> mObbPathToStateMap = new HashMap<String, ObbState>();
411
412 class ObbState implements IBinder.DeathRecipient {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700413 public ObbState(String rawPath, String canonicalPath, int callingUid,
414 IObbActionListener token, int nonce) {
415 this.rawPath = rawPath;
416 this.canonicalPath = canonicalPath.toString();
417
418 final int userId = UserHandle.getUserId(callingUid);
419 this.ownerPath = buildObbPath(canonicalPath, userId, false);
420 this.voldPath = buildObbPath(canonicalPath, userId, true);
421
422 this.ownerGid = UserHandle.getSharedAppGid(callingUid);
Kenny Rootaf9d6672010-10-08 09:21:39 -0700423 this.token = token;
424 this.nonce = nonce;
Kenny Roota02b8b02010-08-05 16:14:17 -0700425 }
426
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700427 final String rawPath;
428 final String canonicalPath;
429 final String ownerPath;
430 final String voldPath;
Kenny Roota02b8b02010-08-05 16:14:17 -0700431
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700432 final int ownerGid;
Kenny Roota02b8b02010-08-05 16:14:17 -0700433
Kenny Rootaf9d6672010-10-08 09:21:39 -0700434 // Token of remote Binder caller
435 final IObbActionListener token;
436
437 // Identifier to pass back to the token
438 final int nonce;
Kenny Roota02b8b02010-08-05 16:14:17 -0700439
Kenny Root735de3b2010-09-30 14:11:39 -0700440 public IBinder getBinder() {
441 return token.asBinder();
442 }
443
Kenny Roota02b8b02010-08-05 16:14:17 -0700444 @Override
445 public void binderDied() {
446 ObbAction action = new UnmountObbAction(this, true);
447 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
Kenny Root735de3b2010-09-30 14:11:39 -0700448 }
Kenny Roota02b8b02010-08-05 16:14:17 -0700449
Kenny Root5919ac62010-10-05 09:49:40 -0700450 public void link() throws RemoteException {
451 getBinder().linkToDeath(this, 0);
452 }
453
454 public void unlink() {
Kenny Root735de3b2010-09-30 14:11:39 -0700455 getBinder().unlinkToDeath(this, 0);
Kenny Roota02b8b02010-08-05 16:14:17 -0700456 }
Kenny Root38cf8862010-09-26 14:18:51 -0700457
458 @Override
459 public String toString() {
460 StringBuilder sb = new StringBuilder("ObbState{");
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700461 sb.append("rawPath=").append(rawPath);
462 sb.append(",canonicalPath=").append(canonicalPath);
463 sb.append(",ownerPath=").append(ownerPath);
464 sb.append(",voldPath=").append(voldPath);
465 sb.append(",ownerGid=").append(ownerGid);
466 sb.append(",token=").append(token);
467 sb.append(",binder=").append(getBinder());
Kenny Root38cf8862010-09-26 14:18:51 -0700468 sb.append('}');
469 return sb.toString();
470 }
Kenny Roota02b8b02010-08-05 16:14:17 -0700471 }
472
473 // OBB Action Handler
474 final private ObbActionHandler mObbActionHandler;
475
476 // OBB action handler messages
477 private static final int OBB_RUN_ACTION = 1;
478 private static final int OBB_MCS_BOUND = 2;
479 private static final int OBB_MCS_UNBIND = 3;
480 private static final int OBB_MCS_RECONNECT = 4;
Kenny Rootaf9d6672010-10-08 09:21:39 -0700481 private static final int OBB_FLUSH_MOUNT_STATE = 5;
Kenny Roota02b8b02010-08-05 16:14:17 -0700482
483 /*
484 * Default Container Service information
485 */
486 static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
487 "com.android.defcontainer", "com.android.defcontainer.DefaultContainerService");
488
489 final private DefaultContainerConnection mDefContainerConn = new DefaultContainerConnection();
490
491 class DefaultContainerConnection implements ServiceConnection {
Jeff Sharkey48877892015-03-18 11:27:19 -0700492 @Override
Kenny Roota02b8b02010-08-05 16:14:17 -0700493 public void onServiceConnected(ComponentName name, IBinder service) {
494 if (DEBUG_OBB)
495 Slog.i(TAG, "onServiceConnected");
496 IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(service);
497 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_MCS_BOUND, imcs));
498 }
499
Jeff Sharkey48877892015-03-18 11:27:19 -0700500 @Override
Kenny Roota02b8b02010-08-05 16:14:17 -0700501 public void onServiceDisconnected(ComponentName name) {
502 if (DEBUG_OBB)
503 Slog.i(TAG, "onServiceDisconnected");
504 }
505 };
506
507 // Used in the ObbActionHandler
508 private IMediaContainerService mContainerService = null;
Kenny Root02c87302010-07-01 08:10:18 -0700509
Christopher Tate7265abe2014-11-21 13:54:45 -0800510 // Last fstrim operation tracking
511 private static final String LAST_FSTRIM_FILE = "last-fstrim";
512 private final File mLastMaintenanceFile;
513 private long mLastMaintenance;
514
Kenny Root02c87302010-07-01 08:10:18 -0700515 // Handler messages
Jeff Sharkey48877892015-03-18 11:27:19 -0700516 private static final int H_SYSTEM_READY = 1;
517 private static final int H_DAEMON_CONNECTED = 2;
518 private static final int H_SHUTDOWN = 3;
519 private static final int H_FSTRIM = 4;
520 private static final int H_VOLUME_MOUNT = 5;
521 private static final int H_VOLUME_BROADCAST = 6;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800522
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400523 class MountServiceHandler extends Handler {
Jeff Sharkey48877892015-03-18 11:27:19 -0700524 public MountServiceHandler(Looper looper) {
525 super(looper);
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400526 }
527
Jason parks5af0b912010-11-29 09:05:25 -0600528 @Override
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800529 public void handleMessage(Message msg) {
530 switch (msg.what) {
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700531 case H_SYSTEM_READY: {
Jeff Sharkey48877892015-03-18 11:27:19 -0700532 handleSystemReady();
533 break;
534 }
535 case H_DAEMON_CONNECTED: {
536 handleDaemonConnected();
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700537 break;
538 }
Christopher Tated417d622013-08-19 16:14:25 -0700539 case H_FSTRIM: {
Jeff Sharkey1783f142015-04-17 10:52:51 -0700540 if (!isReady()) {
541 Slog.i(TAG, "fstrim requested, but no daemon connection yet; trying again");
Christopher Tate7618db12015-04-28 16:32:55 -0700542 sendMessageDelayed(obtainMessage(H_FSTRIM, msg.obj),
543 DateUtils.SECOND_IN_MILLIS);
544 break;
Jeff Sharkey1783f142015-04-17 10:52:51 -0700545 }
546
Christopher Tated417d622013-08-19 16:14:25 -0700547 Slog.i(TAG, "Running fstrim idle maintenance");
Christopher Tate7265abe2014-11-21 13:54:45 -0800548
549 // Remember when we kicked it off
550 try {
551 mLastMaintenance = System.currentTimeMillis();
552 mLastMaintenanceFile.setLastModified(mLastMaintenance);
553 } catch (Exception e) {
554 Slog.e(TAG, "Unable to record last fstrim!");
555 }
556
Christopher Tated417d622013-08-19 16:14:25 -0700557 try {
558 // This method must be run on the main (handler) thread,
559 // so it is safe to directly call into vold.
560 mConnector.execute("fstrim", "dotrim");
561 EventLogTags.writeFstrimStart(SystemClock.elapsedRealtime());
562 } catch (NativeDaemonConnectorException ndce) {
563 Slog.e(TAG, "Failed to run fstrim!");
564 }
Christopher Tate7265abe2014-11-21 13:54:45 -0800565
Christopher Tated417d622013-08-19 16:14:25 -0700566 // invoke the completion callback, if any
567 Runnable callback = (Runnable) msg.obj;
568 if (callback != null) {
569 callback.run();
570 }
571 break;
572 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700573 case H_SHUTDOWN: {
574 final IMountShutdownObserver obs = (IMountShutdownObserver) msg.obj;
575 boolean success = false;
576 try {
577 success = mConnector.execute("volume", "shutdown").isClassOk();
578 } catch (NativeDaemonConnectorException ignored) {
579 }
580 if (obs != null) {
581 try {
582 obs.onShutDownComplete(success ? 0 : -1);
583 } catch (RemoteException ignored) {
584 }
585 }
586 break;
587 }
588 case H_VOLUME_MOUNT: {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700589 final VolumeInfo vol = (VolumeInfo) msg.obj;
Jeff Sharkey48877892015-03-18 11:27:19 -0700590 try {
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700591 mConnector.execute("volume", "mount", vol.id, vol.mountFlags,
592 vol.mountUserId);
Jeff Sharkey48877892015-03-18 11:27:19 -0700593 } catch (NativeDaemonConnectorException ignored) {
594 }
595 break;
596 }
597 case H_VOLUME_BROADCAST: {
598 final StorageVolume userVol = (StorageVolume) msg.obj;
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700599 final String envState = userVol.getState();
600 Slog.d(TAG, "Volume " + userVol.getId() + " broadcasting " + envState + " to "
Jeff Sharkey48877892015-03-18 11:27:19 -0700601 + userVol.getOwner());
602
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700603 final String action = VolumeInfo.getBroadcastForEnvironment(envState);
Jeff Sharkey48877892015-03-18 11:27:19 -0700604 if (action != null) {
605 final Intent intent = new Intent(action,
606 Uri.fromFile(userVol.getPathFile()));
607 intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, userVol);
608 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
609 mContext.sendBroadcastAsUser(intent, userVol.getOwner());
610 }
611 break;
612 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800613 }
614 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700615 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700616
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700617 private final Handler mHandler;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800618
Jeff Sharkey56e62932015-03-21 20:41:00 -0700619 @Override
620 public void waitForAsecScan() {
Jeff Sharkey48877892015-03-18 11:27:19 -0700621 waitForLatch(mAsecsScanned, "mAsecsScanned");
Kenny Root51a573c2012-05-17 13:30:28 -0700622 }
623
San Mehat207e5382010-02-04 20:46:54 -0800624 private void waitForReady() {
Jeff Sharkey48877892015-03-18 11:27:19 -0700625 waitForLatch(mConnectedSignal, "mConnectedSignal");
Kenny Root51a573c2012-05-17 13:30:28 -0700626 }
627
Jeff Sharkey48877892015-03-18 11:27:19 -0700628 private void waitForLatch(CountDownLatch latch, String condition) {
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700629 while (true) {
Kenny Root51a573c2012-05-17 13:30:28 -0700630 try {
631 if (latch.await(5000, TimeUnit.MILLISECONDS)) {
San Mehat207e5382010-02-04 20:46:54 -0800632 return;
Kenny Root51a573c2012-05-17 13:30:28 -0700633 } else {
634 Slog.w(TAG, "Thread " + Thread.currentThread().getName()
Jeff Sharkey48877892015-03-18 11:27:19 -0700635 + " still waiting for " + condition + "...");
San Mehat207e5382010-02-04 20:46:54 -0800636 }
Kenny Root51a573c2012-05-17 13:30:28 -0700637 } catch (InterruptedException e) {
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700638 Slog.w(TAG, "Interrupt while waiting for " + condition);
San Mehat207e5382010-02-04 20:46:54 -0800639 }
San Mehat207e5382010-02-04 20:46:54 -0800640 }
San Mehat1f6301e2010-01-07 22:40:27 -0800641 }
Kenny Root02c87302010-07-01 08:10:18 -0700642
Paul Lawrence945490c2014-03-27 16:37:28 +0000643 private boolean isReady() {
644 try {
645 return mConnectedSignal.await(0, TimeUnit.MILLISECONDS);
646 } catch (InterruptedException e) {
647 return false;
648 }
649 }
650
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700651 private void handleSystemReady() {
Jeff Sharkey48877892015-03-18 11:27:19 -0700652 resetIfReadyAndConnected();
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700653
Jeff Sharkey48877892015-03-18 11:27:19 -0700654 // Start scheduling nominally-daily fstrim operations
Christopher Tate115afda2014-06-06 19:06:26 -0700655 MountServiceIdler.scheduleIdlePass(mContext);
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700656 }
657
Jeff Sharkey48877892015-03-18 11:27:19 -0700658 private void resetIfReadyAndConnected() {
659 Slog.d(TAG, "Thinking about reset, mSystemReady=" + mSystemReady
660 + ", mDaemonConnected=" + mDaemonConnected);
661 if (mSystemReady && mDaemonConnected) {
662 mDisks.clear();
663 mVolumes.clear();
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700664
Jeff Sharkey59d577a2015-04-11 21:27:21 -0700665 // Create a stub volume that represents internal storage
666 final VolumeInfo internal = new VolumeInfo(VolumeInfo.ID_PRIVATE_INTERNAL,
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700667 VolumeInfo.TYPE_PRIVATE, null, 0);
Jeff Sharkey59d577a2015-04-11 21:27:21 -0700668 internal.state = VolumeInfo.STATE_MOUNTED;
669 internal.path = Environment.getDataDirectory().getAbsolutePath();
670 mVolumes.put(internal.id, internal);
671
Jeff Sharkey48877892015-03-18 11:27:19 -0700672 try {
673 mConnector.execute("volume", "reset");
Jeff Sharkey50a05452015-04-29 11:24:52 -0700674 for (int userId : mStartedUsers) {
675 mConnector.execute("volume", "start_user", userId);
676 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700677 } catch (NativeDaemonConnectorException e) {
678 Slog.w(TAG, "Failed to reset vold", e);
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700679 }
680 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700681 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700682
Jeff Sharkey48877892015-03-18 11:27:19 -0700683 private void onStartUser(int userId) {
684 Slog.d(TAG, "onStartUser " + userId);
685
686 // We purposefully block here to make sure that user-specific
687 // staging area is ready so it's ready for zygote-forked apps to
688 // bind mount against.
689 try {
690 mConnector.execute("volume", "start_user", userId);
691 } catch (NativeDaemonConnectorException ignored) {
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700692 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700693
694 // Record user as started so newly mounted volumes kick off events
695 // correctly, then synthesize events for any already-mounted volumes.
696 synchronized (mVolumes) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700697 for (int i = 0; i < mVolumes.size(); i++) {
698 final VolumeInfo vol = mVolumes.valueAt(i);
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700699 if (vol.isVisibleToUser(userId) && vol.isMountedReadable()) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700700 final StorageVolume userVol = vol.buildStorageVolume(mContext, userId);
Jeff Sharkey48877892015-03-18 11:27:19 -0700701 mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget();
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700702
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700703 final String envState = VolumeInfo.getEnvironmentForState(vol.getState());
704 mCallbacks.notifyStorageStateChanged(userVol.getPath(), envState, envState);
Jeff Sharkey48877892015-03-18 11:27:19 -0700705 }
706 }
707 mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userId);
708 }
709 }
710
711 private void onCleanupUser(int userId) {
712 Slog.d(TAG, "onCleanupUser " + userId);
713
714 try {
715 mConnector.execute("volume", "cleanup_user", userId);
716 } catch (NativeDaemonConnectorException ignored) {
717 }
718
719 synchronized (mVolumes) {
720 mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userId);
721 }
722 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700723
Christopher Tated417d622013-08-19 16:14:25 -0700724 void runIdleMaintenance(Runnable callback) {
725 mHandler.sendMessage(mHandler.obtainMessage(H_FSTRIM, callback));
726 }
727
Christopher Tate7265abe2014-11-21 13:54:45 -0800728 // Binder entry point for kicking off an immediate fstrim
729 @Override
730 public void runMaintenance() {
Jeff Sharkey48877892015-03-18 11:27:19 -0700731 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
Christopher Tate7265abe2014-11-21 13:54:45 -0800732 runIdleMaintenance(null);
733 }
734
735 @Override
736 public long lastMaintenance() {
737 return mLastMaintenance;
738 }
739
San Mehat4270e1e2010-01-29 05:32:19 -0800740 /**
San Mehat4270e1e2010-01-29 05:32:19 -0800741 * Callback from NativeDaemonConnector
742 */
Jeff Sharkey48877892015-03-18 11:27:19 -0700743 @Override
San Mehat4270e1e2010-01-29 05:32:19 -0800744 public void onDaemonConnected() {
Jeff Sharkey48877892015-03-18 11:27:19 -0700745 mDaemonConnected = true;
746 mHandler.obtainMessage(H_DAEMON_CONNECTED).sendToTarget();
747 }
748
749 private void handleDaemonConnected() {
750 resetIfReadyAndConnected();
751
San Mehat4270e1e2010-01-29 05:32:19 -0800752 /*
Jeff Sharkey48877892015-03-18 11:27:19 -0700753 * Now that we've done our initialization, release
754 * the hounds!
San Mehat4270e1e2010-01-29 05:32:19 -0800755 */
Jeff Sharkey48877892015-03-18 11:27:19 -0700756 mConnectedSignal.countDown();
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400757
Jeff Sharkey48877892015-03-18 11:27:19 -0700758 // On an encrypted device we can't see system properties yet, so pull
759 // the system locale out of the mount service.
760 if ("".equals(SystemProperties.get("vold.encrypt_progress"))) {
761 copyLocaleFromMountService();
762 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700763
Jeff Sharkey48877892015-03-18 11:27:19 -0700764 // Let package manager load internal ASECs.
765 mPms.scanAvailableAsecs();
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400766
Jeff Sharkey48877892015-03-18 11:27:19 -0700767 // Notify people waiting for ASECs to be scanned that it's done.
768 mAsecsScanned.countDown();
San Mehat4270e1e2010-01-29 05:32:19 -0800769 }
770
Elliott Hughesf839b4f2014-09-26 12:30:47 -0700771 private void copyLocaleFromMountService() {
772 String systemLocale;
773 try {
774 systemLocale = getField(StorageManager.SYSTEM_LOCALE_KEY);
775 } catch (RemoteException e) {
776 return;
777 }
778 if (TextUtils.isEmpty(systemLocale)) {
779 return;
780 }
781
782 Slog.d(TAG, "Got locale " + systemLocale + " from mount service");
783 Locale locale = Locale.forLanguageTag(systemLocale);
784 Configuration config = new Configuration();
785 config.setLocale(locale);
786 try {
787 ActivityManagerNative.getDefault().updateConfiguration(config);
788 } catch (RemoteException e) {
789 Slog.e(TAG, "Error setting system locale from mount service", e);
790 }
Elliott Hughes9c33f282014-10-13 12:39:56 -0700791
792 // Temporary workaround for http://b/17945169.
793 Slog.d(TAG, "Setting system properties to " + systemLocale + " from mount service");
Narayan Kamathd30dbb82015-01-15 14:48:15 +0000794 SystemProperties.set("persist.sys.locale", locale.toLanguageTag());
Elliott Hughesf839b4f2014-09-26 12:30:47 -0700795 }
796
San Mehat4270e1e2010-01-29 05:32:19 -0800797 /**
San Mehat4270e1e2010-01-29 05:32:19 -0800798 * Callback from NativeDaemonConnector
799 */
Jeff Sharkey48877892015-03-18 11:27:19 -0700800 @Override
Dianne Hackborn77b987f2014-02-26 16:20:52 -0800801 public boolean onCheckHoldWakeLock(int code) {
802 return false;
803 }
804
805 /**
806 * Callback from NativeDaemonConnector
807 */
Jeff Sharkey48877892015-03-18 11:27:19 -0700808 @Override
San Mehat4270e1e2010-01-29 05:32:19 -0800809 public boolean onEvent(int code, String raw, String[] cooked) {
Jeff Sharkey48877892015-03-18 11:27:19 -0700810 synchronized (mLock) {
811 return onEventLocked(code, raw, cooked);
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800812 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700813 }
Jeff Sharkey5aca2b82013-10-16 16:21:54 -0700814
Jeff Sharkey48877892015-03-18 11:27:19 -0700815 private boolean onEventLocked(int code, String raw, String[] cooked) {
816 switch (code) {
817 case VoldResponseCode.DISK_CREATED: {
818 if (cooked.length != 3) break;
819 final String id = cooked[1];
Jeff Sharkey74acbbb2015-04-21 12:14:03 -0700820 int flags = Integer.parseInt(cooked[2]);
Jeff Sharkey4c099d02015-05-15 13:45:00 -0700821 if (SystemProperties.getBoolean(StorageManager.PROP_FORCE_ADOPTABLE, false)
822 || mForceAdoptable) {
Jeff Sharkey74acbbb2015-04-21 12:14:03 -0700823 flags |= DiskInfo.FLAG_ADOPTABLE;
824 }
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700825 mDisks.put(id, new DiskInfo(id, flags));
Jeff Sharkey48877892015-03-18 11:27:19 -0700826 break;
Jeff Sharkey5aca2b82013-10-16 16:21:54 -0700827 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700828 case VoldResponseCode.DISK_SIZE_CHANGED: {
829 if (cooked.length != 3) break;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700830 final DiskInfo disk = mDisks.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -0700831 if (disk != null) {
832 disk.size = Long.parseLong(cooked[2]);
San Mehat4270e1e2010-01-29 05:32:19 -0800833 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700834 break;
835 }
836 case VoldResponseCode.DISK_LABEL_CHANGED: {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700837 final DiskInfo disk = mDisks.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -0700838 if (disk != null) {
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700839 final StringBuilder builder = new StringBuilder();
840 for (int i = 2; i < cooked.length; i++) {
841 builder.append(cooked[i]).append(' ');
842 }
843 disk.label = builder.toString().trim();
Jeff Sharkey48877892015-03-18 11:27:19 -0700844 }
845 break;
846 }
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700847 case VoldResponseCode.DISK_SCANNED: {
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700848 if (cooked.length != 2) break;
849 final DiskInfo disk = mDisks.get(cooked[1]);
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700850 if (disk != null) {
851 onDiskScannedLocked(disk);
852 }
Jeff Sharkey59d577a2015-04-11 21:27:21 -0700853 break;
854 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700855 case VoldResponseCode.DISK_DESTROYED: {
856 if (cooked.length != 2) break;
857 mDisks.remove(cooked[1]);
858 break;
859 }
San Mehat4270e1e2010-01-29 05:32:19 -0800860
Jeff Sharkey48877892015-03-18 11:27:19 -0700861 case VoldResponseCode.VOLUME_CREATED: {
Jeff Sharkey48877892015-03-18 11:27:19 -0700862 final String id = cooked[1];
863 final int type = Integer.parseInt(cooked[2]);
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700864 final String diskId = (cooked.length == 4) ? cooked[3] : null;
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700865 final DiskInfo disk = mDisks.get(diskId);
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700866 final int mtpIndex = allocateMtpIndex(id);
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700867 final VolumeInfo vol = new VolumeInfo(id, type, disk, mtpIndex);
Jeff Sharkey48877892015-03-18 11:27:19 -0700868 mVolumes.put(id, vol);
869 onVolumeCreatedLocked(vol);
870 break;
871 }
872 case VoldResponseCode.VOLUME_STATE_CHANGED: {
873 if (cooked.length != 3) break;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700874 final VolumeInfo vol = mVolumes.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -0700875 if (vol != null) {
876 final int oldState = vol.state;
877 final int newState = Integer.parseInt(cooked[2]);
878 vol.state = newState;
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700879 onVolumeStateChangedLocked(vol, oldState, newState);
Jeff Sharkey48877892015-03-18 11:27:19 -0700880 }
881 break;
882 }
883 case VoldResponseCode.VOLUME_FS_TYPE_CHANGED: {
884 if (cooked.length != 3) break;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700885 final VolumeInfo vol = mVolumes.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -0700886 if (vol != null) {
887 vol.fsType = cooked[2];
888 }
889 break;
890 }
891 case VoldResponseCode.VOLUME_FS_UUID_CHANGED: {
892 if (cooked.length != 3) break;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700893 final VolumeInfo vol = mVolumes.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -0700894 if (vol != null) {
895 vol.fsUuid = cooked[2];
896 }
897 break;
898 }
899 case VoldResponseCode.VOLUME_FS_LABEL_CHANGED: {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700900 final VolumeInfo vol = mVolumes.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -0700901 if (vol != null) {
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700902 final StringBuilder builder = new StringBuilder();
903 for (int i = 2; i < cooked.length; i++) {
904 builder.append(cooked[i]).append(' ');
905 }
906 vol.fsLabel = builder.toString().trim();
Jeff Sharkey48877892015-03-18 11:27:19 -0700907 }
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700908 // TODO: notify listeners that label changed
Jeff Sharkey48877892015-03-18 11:27:19 -0700909 break;
910 }
911 case VoldResponseCode.VOLUME_PATH_CHANGED: {
912 if (cooked.length != 3) break;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700913 final VolumeInfo vol = mVolumes.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -0700914 if (vol != null) {
915 vol.path = cooked[2];
916 }
917 break;
918 }
Jeff Sharkey50a05452015-04-29 11:24:52 -0700919 case VoldResponseCode.VOLUME_INTERNAL_PATH_CHANGED: {
920 if (cooked.length != 3) break;
921 final VolumeInfo vol = mVolumes.get(cooked[1]);
922 if (vol != null) {
923 vol.internalPath = cooked[2];
924 }
925 break;
926 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700927 case VoldResponseCode.VOLUME_DESTROYED: {
928 if (cooked.length != 2) break;
929 mVolumes.remove(cooked[1]);
930 break;
931 }
San Mehat4270e1e2010-01-29 05:32:19 -0800932
Jeff Sharkey275e3e42015-04-24 16:10:32 -0700933 case VoldResponseCode.MOVE_STATUS: {
934 final int status = Integer.parseInt(cooked[1]);
935 onMoveStatusLocked(status);
936 break;
937 }
938
Jeff Sharkey9756d752015-05-14 21:07:42 -0700939 case VoldResponseCode.BENCHMARK_RESULT: {
940 final DropBoxManager dropBox = mContext.getSystemService(DropBoxManager.class);
941 dropBox.addText(TAG_STORAGE_BENCHMARK, raw);
942 break;
943 }
944
Jeff Sharkey48877892015-03-18 11:27:19 -0700945 case VoldResponseCode.FstrimCompleted: {
Svetoslav9e814a82013-04-30 10:43:56 -0700946 EventLogTags.writeFstrimFinish(SystemClock.elapsedRealtime());
Jeff Sharkey48877892015-03-18 11:27:19 -0700947 break;
San Mehat4270e1e2010-01-29 05:32:19 -0800948 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700949 default: {
950 Slog.d(TAG, "Unhandled vold event " + code);
Mike Lockwooda5250c92011-05-23 13:44:04 -0400951 }
San Mehat4270e1e2010-01-29 05:32:19 -0800952 }
953
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400954 return true;
San Mehat4270e1e2010-01-29 05:32:19 -0800955 }
956
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700957 private void onDiskScannedLocked(DiskInfo disk) {
Jeff Sharkey620b32b2015-04-23 19:36:02 -0700958 final Intent intent = new Intent(DiskInfo.ACTION_DISK_SCANNED);
959 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
960 mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
961 android.Manifest.permission.WRITE_MEDIA_STORAGE);
962
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700963 final CountDownLatch latch = mDiskScanLatches.remove(disk.id);
964 if (latch != null) {
965 latch.countDown();
966 }
967
Jeff Sharkey620b32b2015-04-23 19:36:02 -0700968 int volumeCount = 0;
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700969 for (int i = 0; i < mVolumes.size(); i++) {
970 final VolumeInfo vol = mVolumes.valueAt(i);
971 if (Objects.equals(disk.id, vol.getDiskId())) {
Jeff Sharkey620b32b2015-04-23 19:36:02 -0700972 volumeCount++;
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700973 }
974 }
975
Jeff Sharkeyf5a6bd72015-05-19 14:42:38 -0700976 disk.volumeCount = volumeCount;
Jeff Sharkey620b32b2015-04-23 19:36:02 -0700977 mCallbacks.notifyDiskScanned(disk, volumeCount);
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700978 }
979
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700980 private void onVolumeCreatedLocked(VolumeInfo vol) {
Jeff Sharkey275e3e42015-04-24 16:10:32 -0700981 if (vol.type == VolumeInfo.TYPE_EMULATED) {
982 final StorageManager storage = mContext.getSystemService(StorageManager.class);
983 final VolumeInfo privateVol = storage.findPrivateForEmulated(vol);
984
985 if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, mPrimaryStorageUuid)
986 && VolumeInfo.ID_PRIVATE_INTERNAL.equals(privateVol.id)) {
987 Slog.v(TAG, "Found primary storage at " + vol);
988 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
989 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
990 mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
991
992 } else if (Objects.equals(privateVol.fsUuid, mPrimaryStorageUuid)) {
993 Slog.v(TAG, "Found primary storage at " + vol);
994 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
995 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
996 mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
997 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700998
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700999 } else if (vol.type == VolumeInfo.TYPE_PUBLIC) {
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001000 // TODO: only look at first public partition
1001 if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, mPrimaryStorageUuid)
1002 && vol.disk.isDefaultPrimary()) {
1003 Slog.v(TAG, "Found primary storage at " + vol);
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07001004 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
1005 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
San Mehat4270e1e2010-01-29 05:32:19 -08001006 }
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07001007
1008 // Adoptable public disks are visible to apps, since they meet
1009 // public API requirement of being in a stable location.
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001010 if (vol.disk.isAdoptable()) {
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07001011 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
1012 }
1013
1014 vol.mountUserId = UserHandle.USER_OWNER;
Jeff Sharkey48877892015-03-18 11:27:19 -07001015 mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
San Mehat4270e1e2010-01-29 05:32:19 -08001016
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -07001017 } else if (vol.type == VolumeInfo.TYPE_PRIVATE) {
1018 mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
1019
San Mehat4270e1e2010-01-29 05:32:19 -08001020 } else {
Jeff Sharkey48877892015-03-18 11:27:19 -07001021 Slog.d(TAG, "Skipping automatic mounting of " + vol);
San Mehat4270e1e2010-01-29 05:32:19 -08001022 }
1023 }
1024
Jeff Sharkeye6c04f92015-04-18 21:38:05 -07001025 private boolean isBroadcastWorthy(VolumeInfo vol) {
1026 switch (vol.getType()) {
1027 case VolumeInfo.TYPE_PUBLIC:
1028 case VolumeInfo.TYPE_EMULATED:
1029 break;
1030 default:
1031 return false;
1032 }
1033
1034 switch (vol.getState()) {
1035 case VolumeInfo.STATE_MOUNTED:
1036 case VolumeInfo.STATE_MOUNTED_READ_ONLY:
1037 case VolumeInfo.STATE_EJECTING:
1038 case VolumeInfo.STATE_UNMOUNTED:
1039 break;
1040 default:
1041 return false;
1042 }
1043
1044 return true;
1045 }
1046
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001047 private void onVolumeStateChangedLocked(VolumeInfo vol, int oldState, int newState) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001048 // Remember that we saw this volume so we're ready to accept user
1049 // metadata, or so we can annoy them when a private volume is ejected
1050 if (vol.isMountedReadable() && !TextUtils.isEmpty(vol.fsUuid)) {
1051 if (!mRecords.containsKey(vol.fsUuid)) {
1052 final VolumeRecord rec = new VolumeRecord(vol.type, vol.fsUuid);
1053 if (vol.type == VolumeInfo.TYPE_PRIVATE) {
1054 rec.nickname = vol.disk.getDescription();
1055 }
1056 mRecords.put(rec.fsUuid, rec);
1057 writeSettingsLocked();
1058 }
1059 }
1060
Jeff Sharkeye6c04f92015-04-18 21:38:05 -07001061 mCallbacks.notifyVolumeStateChanged(vol, oldState, newState);
1062
1063 if (isBroadcastWorthy(vol)) {
1064 final Intent intent = new Intent(VolumeInfo.ACTION_VOLUME_STATE_CHANGED);
1065 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Jeff Sharkey620b32b2015-04-23 19:36:02 -07001066 mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
1067 android.Manifest.permission.WRITE_MEDIA_STORAGE);
Jeff Sharkeye6c04f92015-04-18 21:38:05 -07001068 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001069
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001070 final String oldStateEnv = VolumeInfo.getEnvironmentForState(oldState);
1071 final String newStateEnv = VolumeInfo.getEnvironmentForState(newState);
Emily Bernier92aa5a22014-07-07 10:11:48 -04001072
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001073 if (!Objects.equals(oldStateEnv, newStateEnv)) {
1074 // Kick state changed event towards all started users. Any users
1075 // started after this point will trigger additional
1076 // user-specific broadcasts.
1077 for (int userId : mStartedUsers) {
1078 if (vol.isVisibleToUser(userId)) {
1079 final StorageVolume userVol = vol.buildStorageVolume(mContext, userId);
1080 mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget();
Jeff Sharkey48877892015-03-18 11:27:19 -07001081
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001082 mCallbacks.notifyStorageStateChanged(userVol.getPath(), oldStateEnv,
1083 newStateEnv);
San Mehat4270e1e2010-01-29 05:32:19 -08001084 }
1085 }
1086 }
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001087
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07001088 if (vol.type == VolumeInfo.TYPE_PUBLIC && vol.state == VolumeInfo.STATE_EJECTING) {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001089 // TODO: this should eventually be handled by new ObbVolume state changes
1090 /*
1091 * Some OBBs might have been unmounted when this volume was
1092 * unmounted, so send a message to the handler to let it know to
1093 * remove those from the list of mounted OBBS.
1094 */
1095 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(
1096 OBB_FLUSH_MOUNT_STATE, vol.path));
1097 }
San Mehat4270e1e2010-01-29 05:32:19 -08001098 }
1099
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001100 private void onMoveStatusLocked(int status) {
1101 if (mMoveCallback == null) {
1102 Slog.w(TAG, "Odd, status but no move requested");
1103 return;
1104 }
1105
1106 // TODO: estimate remaining time
1107 try {
Jeff Sharkey50a05452015-04-29 11:24:52 -07001108 mMoveCallback.onStatusChanged(-1, status, -1);
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001109 } catch (RemoteException ignored) {
1110 }
1111
1112 // We've finished copying and we're about to clean up old data, so
1113 // remember that move was successful if we get rebooted
1114 if (status == MOVE_STATUS_COPY_FINISHED) {
1115 Slog.d(TAG, "Move to " + mMoveTargetUuid + " copy phase finshed; persisting");
1116
1117 mPrimaryStorageUuid = mMoveTargetUuid;
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001118 writeSettingsLocked();
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001119 }
1120
1121 if (PackageManager.isMoveStatusFinished(status)) {
1122 Slog.d(TAG, "Move to " + mMoveTargetUuid + " finished with status " + status);
1123
1124 mMoveCallback = null;
1125 mMoveTargetUuid = null;
1126 }
1127 }
1128
Jeff Sharkey48877892015-03-18 11:27:19 -07001129 private void enforcePermission(String perm) {
1130 mContext.enforceCallingOrSelfPermission(perm, perm);
Mike Lockwooda5250c92011-05-23 13:44:04 -04001131 }
1132
Jeff Sharkey48877892015-03-18 11:27:19 -07001133 private void enforceUserRestriction(String restriction) {
Emily Bernier92aa5a22014-07-07 10:11:48 -04001134 UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
Jeff Sharkey48877892015-03-18 11:27:19 -07001135 if (um.hasUserRestriction(restriction, Binder.getCallingUserHandle())) {
Emily Bernier92aa5a22014-07-07 10:11:48 -04001136 throw new SecurityException("User has restriction " + restriction);
1137 }
1138 }
1139
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001140 /**
San Mehat207e5382010-02-04 20:46:54 -08001141 * Constructs a new MountService instance
1142 *
1143 * @param context Binder context for this service
1144 */
1145 public MountService(Context context) {
Christopher Tated417d622013-08-19 16:14:25 -07001146 sSelf = this;
1147
San Mehat207e5382010-02-04 20:46:54 -08001148 mContext = context;
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001149 mCallbacks = new Callbacks(FgThread.get().getLooper());
San Mehat207e5382010-02-04 20:46:54 -08001150
San Mehat207e5382010-02-04 20:46:54 -08001151 // XXX: This will go away soon in favor of IMountServiceObserver
1152 mPms = (PackageManagerService) ServiceManager.getService("package");
1153
Dianne Hackbornefa92b22013-05-03 14:11:43 -07001154 HandlerThread hthread = new HandlerThread(TAG);
1155 hthread.start();
1156 mHandler = new MountServiceHandler(hthread.getLooper());
Daniel Sandler5f27ef42010-03-16 15:42:02 -04001157
Kenny Roota02b8b02010-08-05 16:14:17 -07001158 // Add OBB Action Handler to MountService thread.
Dianne Hackborn8d044e82013-04-30 17:24:15 -07001159 mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper());
Kenny Roota02b8b02010-08-05 16:14:17 -07001160
Christopher Tate7265abe2014-11-21 13:54:45 -08001161 // Initialize the last-fstrim tracking if necessary
1162 File dataDir = Environment.getDataDirectory();
1163 File systemDir = new File(dataDir, "system");
1164 mLastMaintenanceFile = new File(systemDir, LAST_FSTRIM_FILE);
1165 if (!mLastMaintenanceFile.exists()) {
1166 // Not setting mLastMaintenance here means that we will force an
1167 // fstrim during reboot following the OTA that installs this code.
1168 try {
1169 (new FileOutputStream(mLastMaintenanceFile)).close();
1170 } catch (IOException e) {
1171 Slog.e(TAG, "Unable to create fstrim record " + mLastMaintenanceFile.getPath());
1172 }
1173 } else {
1174 mLastMaintenance = mLastMaintenanceFile.lastModified();
1175 }
1176
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001177 mSettingsFile = new AtomicFile(
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001178 new File(Environment.getSystemSecureDirectory(), "storage.xml"));
1179
1180 synchronized (mLock) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001181 readSettingsLocked();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001182 }
1183
Marco Nelissenc34ebce2010-02-18 13:39:41 -08001184 /*
Kenny Root305bcbf2010-09-03 07:56:38 -07001185 * Create the connection to vold with a maximum queue of twice the
1186 * amount of containers we'd ever expect to have. This keeps an
1187 * "asec list" from blocking a thread repeatedly.
1188 */
Dianne Hackborn77b987f2014-02-26 16:20:52 -08001189 mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25,
1190 null);
Jeff Sharkey48877892015-03-18 11:27:19 -07001191 mConnector.setDebug(true);
Kenny Root51a573c2012-05-17 13:30:28 -07001192
Kenny Root305bcbf2010-09-03 07:56:38 -07001193 Thread thread = new Thread(mConnector, VOLD_TAG);
San Mehat207e5382010-02-04 20:46:54 -08001194 thread.start();
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -07001195
Kenny Root07714d42011-08-17 17:49:28 -07001196 // Add ourself to the Watchdog monitors if enabled.
1197 if (WATCHDOG_ENABLE) {
1198 Watchdog.getInstance().addMonitor(this);
1199 }
San Mehat207e5382010-02-04 20:46:54 -08001200 }
1201
Jeff Sharkey56e62932015-03-21 20:41:00 -07001202 private void systemReady() {
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001203 mSystemReady = true;
1204 mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget();
1205 }
1206
Jeff Sharkeyfced5342015-05-10 14:53:34 -07001207 private String getDefaultPrimaryStorageUuid() {
1208 if (SystemProperties.getBoolean(StorageManager.PROP_PRIMARY_PHYSICAL, false)) {
1209 return StorageManager.UUID_PRIMARY_PHYSICAL;
1210 } else {
1211 return StorageManager.UUID_PRIVATE_INTERNAL;
1212 }
1213 }
1214
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001215 private void readSettingsLocked() {
1216 mRecords.clear();
Jeff Sharkeyfced5342015-05-10 14:53:34 -07001217 mPrimaryStorageUuid = getDefaultPrimaryStorageUuid();
Jeff Sharkey4c099d02015-05-15 13:45:00 -07001218 mForceAdoptable = false;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001219
1220 FileInputStream fis = null;
1221 try {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001222 fis = mSettingsFile.openRead();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001223 final XmlPullParser in = Xml.newPullParser();
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +01001224 in.setInput(fis, StandardCharsets.UTF_8.name());
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001225
1226 int type;
1227 while ((type = in.next()) != END_DOCUMENT) {
1228 if (type == START_TAG) {
1229 final String tag = in.getName();
Jeff Sharkey620b32b2015-04-23 19:36:02 -07001230 if (TAG_VOLUMES.equals(tag)) {
1231 final int version = readIntAttribute(in, ATTR_VERSION, VERSION_INIT);
Jeff Sharkeyfced5342015-05-10 14:53:34 -07001232 final boolean primaryPhysical = SystemProperties.getBoolean(
1233 StorageManager.PROP_PRIMARY_PHYSICAL, false);
1234 final boolean validAttr = (version >= VERSION_FIX_PRIMARY)
1235 || (version >= VERSION_ADD_PRIMARY && !primaryPhysical);
1236 if (validAttr) {
Jeff Sharkey620b32b2015-04-23 19:36:02 -07001237 mPrimaryStorageUuid = readStringAttribute(in,
1238 ATTR_PRIMARY_STORAGE_UUID);
Jeff Sharkey620b32b2015-04-23 19:36:02 -07001239 }
Jeff Sharkey4c099d02015-05-15 13:45:00 -07001240 mForceAdoptable = readBooleanAttribute(in, ATTR_FORCE_ADOPTABLE, false);
Jeff Sharkey620b32b2015-04-23 19:36:02 -07001241
1242 } else if (TAG_VOLUME.equals(tag)) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001243 final VolumeRecord rec = readVolumeRecord(in);
1244 mRecords.put(rec.fsUuid, rec);
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001245 }
1246 }
1247 }
1248 } catch (FileNotFoundException e) {
1249 // Missing metadata is okay, probably first boot
1250 } catch (IOException e) {
1251 Slog.wtf(TAG, "Failed reading metadata", e);
1252 } catch (XmlPullParserException e) {
1253 Slog.wtf(TAG, "Failed reading metadata", e);
1254 } finally {
1255 IoUtils.closeQuietly(fis);
1256 }
1257 }
1258
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001259 private void writeSettingsLocked() {
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001260 FileOutputStream fos = null;
1261 try {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001262 fos = mSettingsFile.startWrite();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001263
1264 XmlSerializer out = new FastXmlSerializer();
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +01001265 out.setOutput(fos, StandardCharsets.UTF_8.name());
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001266 out.startDocument(null, true);
1267 out.startTag(null, TAG_VOLUMES);
Jeff Sharkeyfced5342015-05-10 14:53:34 -07001268 writeIntAttribute(out, ATTR_VERSION, VERSION_FIX_PRIMARY);
Jeff Sharkey620b32b2015-04-23 19:36:02 -07001269 writeStringAttribute(out, ATTR_PRIMARY_STORAGE_UUID, mPrimaryStorageUuid);
Jeff Sharkey4c099d02015-05-15 13:45:00 -07001270 writeBooleanAttribute(out, ATTR_FORCE_ADOPTABLE, mForceAdoptable);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001271 final int size = mRecords.size();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001272 for (int i = 0; i < size; i++) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001273 final VolumeRecord rec = mRecords.valueAt(i);
1274 writeVolumeRecord(out, rec);
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001275 }
1276 out.endTag(null, TAG_VOLUMES);
1277 out.endDocument();
1278
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001279 mSettingsFile.finishWrite(fos);
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001280 } catch (IOException e) {
1281 if (fos != null) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001282 mSettingsFile.failWrite(fos);
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001283 }
1284 }
1285 }
1286
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001287 public static VolumeRecord readVolumeRecord(XmlPullParser in) throws IOException {
1288 final int type = readIntAttribute(in, ATTR_TYPE);
1289 final String fsUuid = readStringAttribute(in, ATTR_FS_UUID);
1290 final VolumeRecord meta = new VolumeRecord(type, fsUuid);
1291 meta.nickname = readStringAttribute(in, ATTR_NICKNAME);
1292 meta.userFlags = readIntAttribute(in, ATTR_USER_FLAGS);
1293 return meta;
1294 }
1295
1296 public static void writeVolumeRecord(XmlSerializer out, VolumeRecord rec) throws IOException {
1297 out.startTag(null, TAG_VOLUME);
1298 writeIntAttribute(out, ATTR_TYPE, rec.type);
1299 writeStringAttribute(out, ATTR_FS_UUID, rec.fsUuid);
1300 writeStringAttribute(out, ATTR_NICKNAME, rec.nickname);
1301 writeIntAttribute(out, ATTR_USER_FLAGS, rec.userFlags);
1302 out.endTag(null, TAG_VOLUME);
1303 }
1304
San Mehat207e5382010-02-04 20:46:54 -08001305 /**
San Mehat4270e1e2010-01-29 05:32:19 -08001306 * Exposed API calls below here
1307 */
1308
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001309 @Override
San Mehat4270e1e2010-01-29 05:32:19 -08001310 public void registerListener(IMountServiceListener listener) {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001311 mCallbacks.register(listener);
San Mehat4270e1e2010-01-29 05:32:19 -08001312 }
1313
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001314 @Override
San Mehat4270e1e2010-01-29 05:32:19 -08001315 public void unregisterListener(IMountServiceListener listener) {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001316 mCallbacks.unregister(listener);
San Mehat4270e1e2010-01-29 05:32:19 -08001317 }
1318
Jeff Sharkey48877892015-03-18 11:27:19 -07001319 @Override
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -08001320 public void shutdown(final IMountShutdownObserver observer) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001321 enforcePermission(android.Manifest.permission.SHUTDOWN);
San Mehat4270e1e2010-01-29 05:32:19 -08001322
San Mehata5078592010-03-25 09:36:54 -07001323 Slog.i(TAG, "Shutting down");
Jeff Sharkey48877892015-03-18 11:27:19 -07001324 mHandler.obtainMessage(H_SHUTDOWN, observer).sendToTarget();
San Mehat4270e1e2010-01-29 05:32:19 -08001325 }
1326
Jeff Sharkey48877892015-03-18 11:27:19 -07001327 @Override
San Mehatb1043402010-02-05 08:26:50 -08001328 public boolean isUsbMassStorageConnected() {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001329 throw new UnsupportedOperationException();
San Mehatb1043402010-02-05 08:26:50 -08001330 }
1331
Jeff Sharkey48877892015-03-18 11:27:19 -07001332 @Override
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001333 public void setUsbMassStorageEnabled(boolean enable) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001334 throw new UnsupportedOperationException();
San Mehatb1043402010-02-05 08:26:50 -08001335 }
1336
Jeff Sharkey48877892015-03-18 11:27:19 -07001337 @Override
San Mehatb1043402010-02-05 08:26:50 -08001338 public boolean isUsbMassStorageEnabled() {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001339 throw new UnsupportedOperationException();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001340 }
Jason parks9ed98bc2011-01-17 09:58:35 -06001341
Jeff Sharkey48877892015-03-18 11:27:19 -07001342 @Override
San Mehat4270e1e2010-01-29 05:32:19 -08001343 public String getVolumeState(String mountPoint) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001344 throw new UnsupportedOperationException();
San Mehat7fd0fee2009-12-17 07:12:23 -08001345 }
1346
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001347 @Override
Kenny Roote1ff2142010-10-12 11:20:01 -07001348 public boolean isExternalStorageEmulated() {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001349 throw new UnsupportedOperationException();
Kenny Roote1ff2142010-10-12 11:20:01 -07001350 }
1351
Jeff Sharkey48877892015-03-18 11:27:19 -07001352 @Override
San Mehat4270e1e2010-01-29 05:32:19 -08001353 public int mountVolume(String path) {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001354 mount(findVolumeIdForPath(path));
1355 return 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001356 }
1357
Jeff Sharkey48877892015-03-18 11:27:19 -07001358 @Override
Ben Komalo13c71972011-09-07 16:35:56 -07001359 public void unmountVolume(String path, boolean force, boolean removeEncryption) {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001360 unmount(findVolumeIdForPath(path));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001361 }
1362
Jeff Sharkey48877892015-03-18 11:27:19 -07001363 @Override
San Mehat4270e1e2010-01-29 05:32:19 -08001364 public int formatVolume(String path) {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001365 format(findVolumeIdForPath(path));
1366 return 0;
1367 }
1368
1369 @Override
1370 public void mount(String volId) {
1371 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1372 waitForReady();
1373
1374 final VolumeInfo vol = findVolumeById(volId);
1375 if (vol.type == VolumeInfo.TYPE_PUBLIC || vol.type == VolumeInfo.TYPE_PRIVATE) {
1376 enforceUserRestriction(UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA);
1377 }
1378 try {
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07001379 mConnector.execute("volume", "mount", vol.id, vol.mountFlags, vol.mountUserId);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001380 } catch (NativeDaemonConnectorException e) {
1381 throw e.rethrowAsParcelableException();
1382 }
1383 }
1384
1385 @Override
1386 public void unmount(String volId) {
1387 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1388 waitForReady();
1389
1390 final VolumeInfo vol = findVolumeById(volId);
1391
1392 // TODO: expand PMS to know about multiple volumes
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001393 if (vol.isPrimaryPhysical()) {
1394 final long ident = Binder.clearCallingIdentity();
1395 try {
1396 synchronized (mUnmountLock) {
1397 mUnmountSignal = new CountDownLatch(1);
1398 mPms.updateExternalMediaStatus(false, true);
1399 waitForLatch(mUnmountSignal, "mUnmountSignal");
1400 mUnmountSignal = null;
1401 }
1402 } finally {
1403 Binder.restoreCallingIdentity(ident);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001404 }
1405 }
1406
1407 try {
1408 mConnector.execute("volume", "unmount", vol.id);
1409 } catch (NativeDaemonConnectorException e) {
1410 throw e.rethrowAsParcelableException();
1411 }
1412 }
1413
1414 @Override
1415 public void format(String volId) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001416 enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
San Mehat207e5382010-02-04 20:46:54 -08001417 waitForReady();
San Mehat5b77dab2010-01-26 13:28:50 -08001418
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001419 final VolumeInfo vol = findVolumeById(volId);
1420 try {
1421 mConnector.execute("volume", "format", vol.id);
1422 } catch (NativeDaemonConnectorException e) {
1423 throw e.rethrowAsParcelableException();
Jeff Sharkey48877892015-03-18 11:27:19 -07001424 }
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001425 }
1426
1427 @Override
Jeff Sharkey9756d752015-05-14 21:07:42 -07001428 public long benchmark(String volId) {
1429 enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
1430 waitForReady();
1431
1432 try {
1433 final NativeDaemonEvent res = mConnector.execute("volume", "benchmark", volId);
1434 return Long.parseLong(res.getMessage());
1435 } catch (NativeDaemonConnectorException e) {
1436 throw e.rethrowAsParcelableException();
1437 }
1438 }
1439
1440 @Override
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001441 public void partitionPublic(String diskId) {
1442 enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
1443 waitForReady();
1444
Jeff Sharkeyeba260d2015-04-19 14:35:16 -07001445 final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001446 try {
1447 mConnector.execute("volume", "partition", diskId, "public");
1448 } catch (NativeDaemonConnectorException e) {
1449 throw e.rethrowAsParcelableException();
1450 }
Jeff Sharkeyeba260d2015-04-19 14:35:16 -07001451 waitForLatch(latch, "partitionPublic");
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001452 }
1453
1454 @Override
1455 public void partitionPrivate(String diskId) {
1456 enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
1457 waitForReady();
1458
Jeff Sharkeyeba260d2015-04-19 14:35:16 -07001459 final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001460 try {
1461 mConnector.execute("volume", "partition", diskId, "private");
1462 } catch (NativeDaemonConnectorException e) {
1463 throw e.rethrowAsParcelableException();
1464 }
Jeff Sharkeyeba260d2015-04-19 14:35:16 -07001465 waitForLatch(latch, "partitionPrivate");
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001466 }
1467
1468 @Override
1469 public void partitionMixed(String diskId, int ratio) {
1470 enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
1471 waitForReady();
1472
Jeff Sharkeyeba260d2015-04-19 14:35:16 -07001473 final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001474 try {
1475 mConnector.execute("volume", "partition", diskId, "mixed", ratio);
1476 } catch (NativeDaemonConnectorException e) {
1477 throw e.rethrowAsParcelableException();
1478 }
Jeff Sharkeyeba260d2015-04-19 14:35:16 -07001479 waitForLatch(latch, "partitionMixed");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001480 }
1481
Jeff Sharkey48877892015-03-18 11:27:19 -07001482 @Override
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001483 public void setVolumeNickname(String fsUuid, String nickname) {
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001484 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1485 waitForReady();
1486
Jeff Sharkey50a05452015-04-29 11:24:52 -07001487 Preconditions.checkNotNull(fsUuid);
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001488 synchronized (mLock) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001489 final VolumeRecord rec = mRecords.get(fsUuid);
1490 rec.nickname = nickname;
Jeff Sharkey50a05452015-04-29 11:24:52 -07001491 mCallbacks.notifyVolumeRecordChanged(rec);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001492 writeSettingsLocked();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001493 }
1494 }
1495
1496 @Override
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001497 public void setVolumeUserFlags(String fsUuid, int flags, int mask) {
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001498 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1499 waitForReady();
1500
Jeff Sharkey50a05452015-04-29 11:24:52 -07001501 Preconditions.checkNotNull(fsUuid);
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001502 synchronized (mLock) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001503 final VolumeRecord rec = mRecords.get(fsUuid);
1504 rec.userFlags = (rec.userFlags & ~mask) | (flags & mask);
Jeff Sharkey50a05452015-04-29 11:24:52 -07001505 mCallbacks.notifyVolumeRecordChanged(rec);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001506 writeSettingsLocked();
1507 }
1508 }
1509
1510 @Override
1511 public void forgetVolume(String fsUuid) {
1512 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1513 waitForReady();
1514
Jeff Sharkey50a05452015-04-29 11:24:52 -07001515 Preconditions.checkNotNull(fsUuid);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001516 synchronized (mLock) {
1517 mRecords.remove(fsUuid);
Jeff Sharkey50a05452015-04-29 11:24:52 -07001518
1519 // TODO: tell vold to forget keys
1520
1521 // If this had been primary storage, revert back to internal and
1522 // reset vold so we bind into new volume into place.
1523 if (Objects.equals(mPrimaryStorageUuid, fsUuid)) {
Jeff Sharkeyfced5342015-05-10 14:53:34 -07001524 mPrimaryStorageUuid = getDefaultPrimaryStorageUuid();
Jeff Sharkey50a05452015-04-29 11:24:52 -07001525 resetIfReadyAndConnected();
1526 }
1527
1528 mCallbacks.notifyVolumeForgotten(fsUuid);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001529 writeSettingsLocked();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001530 }
1531 }
1532
Jeff Sharkey7d2af542015-05-12 15:27:15 -07001533 @Override
1534 public void forgetAllVolumes() {
1535 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1536 waitForReady();
1537
Jeff Sharkey50a05452015-04-29 11:24:52 -07001538 synchronized (mLock) {
1539 for (int i = 0; i < mRecords.size(); i++) {
1540 final String fsUuid = mRecords.keyAt(i);
1541 mCallbacks.notifyVolumeForgotten(fsUuid);
1542 }
Jeff Sharkey50a05452015-04-29 11:24:52 -07001543 mRecords.clear();
Jeff Sharkey50a05452015-04-29 11:24:52 -07001544
Jeff Sharkeyfced5342015-05-10 14:53:34 -07001545 if (!Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, mPrimaryStorageUuid)) {
1546 mPrimaryStorageUuid = getDefaultPrimaryStorageUuid();
1547 }
1548
1549 writeSettingsLocked();
Jeff Sharkey50a05452015-04-29 11:24:52 -07001550 resetIfReadyAndConnected();
1551 }
1552 }
1553
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001554 @Override
Jeff Sharkey4c099d02015-05-15 13:45:00 -07001555 public void setDebugFlags(int flags, int mask) {
1556 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1557 waitForReady();
1558
1559 synchronized (mLock) {
1560 if ((mask & StorageManager.DEBUG_FORCE_ADOPTABLE) != 0) {
1561 mForceAdoptable = (flags & StorageManager.DEBUG_FORCE_ADOPTABLE) != 0;
1562 }
1563
1564 writeSettingsLocked();
1565 resetIfReadyAndConnected();
1566 }
1567 }
1568
1569 @Override
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001570 public String getPrimaryStorageUuid() {
1571 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1572 waitForReady();
1573
Jeff Sharkey620b32b2015-04-23 19:36:02 -07001574 synchronized (mLock) {
1575 return mPrimaryStorageUuid;
1576 }
1577 }
1578
1579 @Override
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001580 public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) {
1581 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1582 waitForReady();
Jeff Sharkey620b32b2015-04-23 19:36:02 -07001583
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001584 synchronized (mLock) {
Jeff Sharkeyfced5342015-05-10 14:53:34 -07001585 if (Objects.equals(mPrimaryStorageUuid, volumeUuid)) {
1586 throw new IllegalArgumentException("Primary storage already at " + volumeUuid);
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001587 }
1588
1589 if (mMoveCallback != null) {
1590 throw new IllegalStateException("Move already in progress");
1591 }
1592 mMoveCallback = callback;
1593 mMoveTargetUuid = volumeUuid;
1594
Jeff Sharkeyfced5342015-05-10 14:53:34 -07001595 // When moving to/from primary physical volume, we probably just nuked
1596 // the current storage location, so we have nothing to move.
1597 if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, mPrimaryStorageUuid)
1598 || Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
1599 Slog.d(TAG, "Skipping move to/from primary physical");
1600 onMoveStatusLocked(MOVE_STATUS_COPY_FINISHED);
1601 onMoveStatusLocked(PackageManager.MOVE_SUCCEEDED);
1602 resetIfReadyAndConnected();
1603
1604 } else {
1605 final VolumeInfo from = Preconditions.checkNotNull(
1606 findStorageForUuid(mPrimaryStorageUuid));
1607 final VolumeInfo to = Preconditions.checkNotNull(
1608 findStorageForUuid(volumeUuid));
1609
1610 try {
1611 mConnector.execute("volume", "move_storage", from.id, to.id);
1612 } catch (NativeDaemonConnectorException e) {
1613 throw e.rethrowAsParcelableException();
1614 }
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001615 }
Jeff Sharkey620b32b2015-04-23 19:36:02 -07001616 }
1617 }
1618
1619 @Override
Mike Lockwoodecedfdc2011-06-08 15:11:59 -07001620 public int[] getStorageUsers(String path) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001621 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
San Mehatc1b4ce92010-02-16 17:13:03 -08001622 waitForReady();
1623 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001624 final String[] r = NativeDaemonEvent.filterMessageList(
1625 mConnector.executeForList("storage", "users", path),
1626 VoldResponseCode.StorageUsersListResult);
1627
San Mehatc1b4ce92010-02-16 17:13:03 -08001628 // FMT: <pid> <process name>
1629 int[] data = new int[r.length];
1630 for (int i = 0; i < r.length; i++) {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001631 String[] tok = r[i].split(" ");
San Mehatc1b4ce92010-02-16 17:13:03 -08001632 try {
1633 data[i] = Integer.parseInt(tok[0]);
1634 } catch (NumberFormatException nfe) {
San Mehata5078592010-03-25 09:36:54 -07001635 Slog.e(TAG, String.format("Error parsing pid %s", tok[0]));
San Mehatc1b4ce92010-02-16 17:13:03 -08001636 return new int[0];
1637 }
1638 }
1639 return data;
1640 } catch (NativeDaemonConnectorException e) {
San Mehata5078592010-03-25 09:36:54 -07001641 Slog.e(TAG, "Failed to retrieve storage users list", e);
San Mehatc1b4ce92010-02-16 17:13:03 -08001642 return new int[0];
1643 }
1644 }
1645
San Mehatb1043402010-02-05 08:26:50 -08001646 private void warnOnNotMounted() {
Jeff Sharkey48877892015-03-18 11:27:19 -07001647 synchronized (mLock) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001648 for (int i = 0; i < mVolumes.size(); i++) {
1649 final VolumeInfo vol = mVolumes.valueAt(i);
Jeff Sharkey27de30d2015-04-18 16:20:27 -07001650 if (vol.isPrimary() && vol.isMountedWritable()) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001651 // Cool beans, we have a mounted primary volume
1652 return;
1653 }
Jeff Sharkey32ee8312012-09-30 13:21:31 -07001654 }
San Mehatb1043402010-02-05 08:26:50 -08001655 }
Jeff Sharkey48877892015-03-18 11:27:19 -07001656
1657 Slog.w(TAG, "No primary storage mounted!");
San Mehatb1043402010-02-05 08:26:50 -08001658 }
1659
San Mehat4270e1e2010-01-29 05:32:19 -08001660 public String[] getSecureContainerList() {
Jeff Sharkey48877892015-03-18 11:27:19 -07001661 enforcePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat207e5382010-02-04 20:46:54 -08001662 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001663 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001664
San Mehat4270e1e2010-01-29 05:32:19 -08001665 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001666 return NativeDaemonEvent.filterMessageList(
1667 mConnector.executeForList("asec", "list"), VoldResponseCode.AsecListResult);
San Mehat4270e1e2010-01-29 05:32:19 -08001668 } catch (NativeDaemonConnectorException e) {
1669 return new String[0];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001670 }
1671 }
San Mehat36972292010-01-06 11:06:32 -08001672
Kenny Root6dceb882012-04-12 14:23:49 -07001673 public int createSecureContainer(String id, int sizeMb, String fstype, String key,
1674 int ownerUid, boolean external) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001675 enforcePermission(android.Manifest.permission.ASEC_CREATE);
San Mehat207e5382010-02-04 20:46:54 -08001676 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001677 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001678
San Mehatb1043402010-02-05 08:26:50 -08001679 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001680 try {
Jeff Sharkey56cd6462013-06-07 15:09:15 -07001681 mConnector.execute("asec", "create", id, sizeMb, fstype, new SensitiveArg(key),
1682 ownerUid, external ? "1" : "0");
San Mehat4270e1e2010-01-29 05:32:19 -08001683 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001684 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001685 }
San Mehata181b212010-02-11 06:50:20 -08001686
1687 if (rc == StorageResultCode.OperationSucceeded) {
1688 synchronized (mAsecMountSet) {
1689 mAsecMountSet.add(id);
1690 }
1691 }
San Mehat4270e1e2010-01-29 05:32:19 -08001692 return rc;
San Mehat36972292010-01-06 11:06:32 -08001693 }
1694
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001695 @Override
1696 public int resizeSecureContainer(String id, int sizeMb, String key) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001697 enforcePermission(android.Manifest.permission.ASEC_CREATE);
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001698 waitForReady();
1699 warnOnNotMounted();
1700
1701 int rc = StorageResultCode.OperationSucceeded;
1702 try {
1703 mConnector.execute("asec", "resize", id, sizeMb, new SensitiveArg(key));
1704 } catch (NativeDaemonConnectorException e) {
1705 rc = StorageResultCode.OperationFailedInternalError;
1706 }
1707 return rc;
1708 }
1709
San Mehat4270e1e2010-01-29 05:32:19 -08001710 public int finalizeSecureContainer(String id) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001711 enforcePermission(android.Manifest.permission.ASEC_CREATE);
San Mehatb1043402010-02-05 08:26:50 -08001712 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001713
San Mehatb1043402010-02-05 08:26:50 -08001714 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001715 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001716 mConnector.execute("asec", "finalize", id);
San Mehata181b212010-02-11 06:50:20 -08001717 /*
1718 * Finalization does a remount, so no need
1719 * to update mAsecMountSet
1720 */
San Mehat4270e1e2010-01-29 05:32:19 -08001721 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001722 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001723 }
San Mehat4270e1e2010-01-29 05:32:19 -08001724 return rc;
San Mehat36972292010-01-06 11:06:32 -08001725 }
1726
Kenny Root6dceb882012-04-12 14:23:49 -07001727 public int fixPermissionsSecureContainer(String id, int gid, String filename) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001728 enforcePermission(android.Manifest.permission.ASEC_CREATE);
Kenny Root6dceb882012-04-12 14:23:49 -07001729 warnOnNotMounted();
1730
1731 int rc = StorageResultCode.OperationSucceeded;
1732 try {
1733 mConnector.execute("asec", "fixperms", id, gid, filename);
1734 /*
1735 * Fix permissions does a remount, so no need to update
1736 * mAsecMountSet
1737 */
1738 } catch (NativeDaemonConnectorException e) {
1739 rc = StorageResultCode.OperationFailedInternalError;
1740 }
1741 return rc;
1742 }
1743
San Mehatd9709982010-02-18 11:43:03 -08001744 public int destroySecureContainer(String id, boolean force) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001745 enforcePermission(android.Manifest.permission.ASEC_DESTROY);
San Mehat207e5382010-02-04 20:46:54 -08001746 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001747 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001748
Kenny Rootaa485402010-09-14 14:49:41 -07001749 /*
1750 * Force a GC to make sure AssetManagers in other threads of the
1751 * system_server are cleaned up. We have to do this since AssetManager
1752 * instances are kept as a WeakReference and it's possible we have files
1753 * open on the external storage.
1754 */
1755 Runtime.getRuntime().gc();
1756
San Mehatb1043402010-02-05 08:26:50 -08001757 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001758 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001759 final Command cmd = new Command("asec", "destroy", id);
1760 if (force) {
1761 cmd.appendArg("force");
1762 }
1763 mConnector.execute(cmd);
San Mehat4270e1e2010-01-29 05:32:19 -08001764 } catch (NativeDaemonConnectorException e) {
San Mehatd9709982010-02-18 11:43:03 -08001765 int code = e.getCode();
1766 if (code == VoldResponseCode.OpFailedStorageBusy) {
1767 rc = StorageResultCode.OperationFailedStorageBusy;
1768 } else {
1769 rc = StorageResultCode.OperationFailedInternalError;
1770 }
San Mehat02735bc2010-01-26 15:18:08 -08001771 }
San Mehata181b212010-02-11 06:50:20 -08001772
1773 if (rc == StorageResultCode.OperationSucceeded) {
1774 synchronized (mAsecMountSet) {
1775 if (mAsecMountSet.contains(id)) {
1776 mAsecMountSet.remove(id);
1777 }
1778 }
1779 }
1780
San Mehat4270e1e2010-01-29 05:32:19 -08001781 return rc;
San Mehat36972292010-01-06 11:06:32 -08001782 }
Jason parks9ed98bc2011-01-17 09:58:35 -06001783
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001784 public int mountSecureContainer(String id, String key, int ownerUid, boolean readOnly) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001785 enforcePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
San Mehat207e5382010-02-04 20:46:54 -08001786 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001787 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001788
San Mehata181b212010-02-11 06:50:20 -08001789 synchronized (mAsecMountSet) {
1790 if (mAsecMountSet.contains(id)) {
1791 return StorageResultCode.OperationFailedStorageMounted;
1792 }
1793 }
1794
San Mehatb1043402010-02-05 08:26:50 -08001795 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001796 try {
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001797 mConnector.execute("asec", "mount", id, new SensitiveArg(key), ownerUid,
1798 readOnly ? "ro" : "rw");
San Mehat4270e1e2010-01-29 05:32:19 -08001799 } catch (NativeDaemonConnectorException e) {
Kenny Rootf0304622010-03-19 19:20:42 -07001800 int code = e.getCode();
1801 if (code != VoldResponseCode.OpFailedStorageBusy) {
1802 rc = StorageResultCode.OperationFailedInternalError;
1803 }
San Mehat02735bc2010-01-26 15:18:08 -08001804 }
San Mehat6cdd9c02010-02-09 14:45:20 -08001805
1806 if (rc == StorageResultCode.OperationSucceeded) {
1807 synchronized (mAsecMountSet) {
1808 mAsecMountSet.add(id);
1809 }
1810 }
San Mehat4270e1e2010-01-29 05:32:19 -08001811 return rc;
San Mehat36972292010-01-06 11:06:32 -08001812 }
1813
San Mehatd9709982010-02-18 11:43:03 -08001814 public int unmountSecureContainer(String id, boolean force) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001815 enforcePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
San Mehat207e5382010-02-04 20:46:54 -08001816 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001817 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001818
San Mehat6cdd9c02010-02-09 14:45:20 -08001819 synchronized (mAsecMountSet) {
1820 if (!mAsecMountSet.contains(id)) {
San Mehata181b212010-02-11 06:50:20 -08001821 return StorageResultCode.OperationFailedStorageNotMounted;
San Mehat6cdd9c02010-02-09 14:45:20 -08001822 }
1823 }
1824
Kenny Rootaa485402010-09-14 14:49:41 -07001825 /*
1826 * Force a GC to make sure AssetManagers in other threads of the
1827 * system_server are cleaned up. We have to do this since AssetManager
1828 * instances are kept as a WeakReference and it's possible we have files
1829 * open on the external storage.
1830 */
1831 Runtime.getRuntime().gc();
1832
San Mehatb1043402010-02-05 08:26:50 -08001833 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001834 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001835 final Command cmd = new Command("asec", "unmount", id);
1836 if (force) {
1837 cmd.appendArg("force");
1838 }
1839 mConnector.execute(cmd);
San Mehat4270e1e2010-01-29 05:32:19 -08001840 } catch (NativeDaemonConnectorException e) {
San Mehatd9709982010-02-18 11:43:03 -08001841 int code = e.getCode();
1842 if (code == VoldResponseCode.OpFailedStorageBusy) {
1843 rc = StorageResultCode.OperationFailedStorageBusy;
1844 } else {
1845 rc = StorageResultCode.OperationFailedInternalError;
1846 }
San Mehat02735bc2010-01-26 15:18:08 -08001847 }
San Mehat6cdd9c02010-02-09 14:45:20 -08001848
1849 if (rc == StorageResultCode.OperationSucceeded) {
1850 synchronized (mAsecMountSet) {
1851 mAsecMountSet.remove(id);
1852 }
1853 }
San Mehat4270e1e2010-01-29 05:32:19 -08001854 return rc;
San Mehat9dba7092010-01-18 06:47:41 -08001855 }
1856
San Mehat6cdd9c02010-02-09 14:45:20 -08001857 public boolean isSecureContainerMounted(String id) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001858 enforcePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat6cdd9c02010-02-09 14:45:20 -08001859 waitForReady();
1860 warnOnNotMounted();
1861
1862 synchronized (mAsecMountSet) {
1863 return mAsecMountSet.contains(id);
1864 }
1865 }
1866
San Mehat4270e1e2010-01-29 05:32:19 -08001867 public int renameSecureContainer(String oldId, String newId) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001868 enforcePermission(android.Manifest.permission.ASEC_RENAME);
San Mehat207e5382010-02-04 20:46:54 -08001869 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001870 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001871
San Mehata181b212010-02-11 06:50:20 -08001872 synchronized (mAsecMountSet) {
San Mehat85451ee2010-02-24 08:54:18 -08001873 /*
Jason parks9ed98bc2011-01-17 09:58:35 -06001874 * Because a mounted container has active internal state which cannot be
San Mehat85451ee2010-02-24 08:54:18 -08001875 * changed while active, we must ensure both ids are not currently mounted.
1876 */
1877 if (mAsecMountSet.contains(oldId) || mAsecMountSet.contains(newId)) {
San Mehata181b212010-02-11 06:50:20 -08001878 return StorageResultCode.OperationFailedStorageMounted;
1879 }
1880 }
1881
San Mehatb1043402010-02-05 08:26:50 -08001882 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001883 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001884 mConnector.execute("asec", "rename", oldId, newId);
San Mehat4270e1e2010-01-29 05:32:19 -08001885 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001886 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001887 }
San Mehata181b212010-02-11 06:50:20 -08001888
San Mehat4270e1e2010-01-29 05:32:19 -08001889 return rc;
San Mehat45f61042010-01-23 08:12:43 -08001890 }
1891
San Mehat4270e1e2010-01-29 05:32:19 -08001892 public String getSecureContainerPath(String id) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001893 enforcePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat207e5382010-02-04 20:46:54 -08001894 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001895 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001896
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001897 final NativeDaemonEvent event;
San Mehat2d66cef2010-03-23 11:12:52 -07001898 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001899 event = mConnector.execute("asec", "path", id);
1900 event.checkCode(VoldResponseCode.AsecPathResult);
1901 return event.getMessage();
San Mehat2d66cef2010-03-23 11:12:52 -07001902 } catch (NativeDaemonConnectorException e) {
1903 int code = e.getCode();
1904 if (code == VoldResponseCode.OpFailedStorageNotFound) {
Fredrik Helmera20c8ef2011-02-09 16:16:10 +01001905 Slog.i(TAG, String.format("Container '%s' not found", id));
1906 return null;
San Mehat22dd86e2010-01-12 12:21:18 -08001907 } else {
San Mehat2d66cef2010-03-23 11:12:52 -07001908 throw new IllegalStateException(String.format("Unexpected response code %d", code));
San Mehat22dd86e2010-01-12 12:21:18 -08001909 }
1910 }
San Mehat22dd86e2010-01-12 12:21:18 -08001911 }
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -07001912
Dianne Hackborn292f8bc2011-06-27 16:27:41 -07001913 public String getSecureContainerFilesystemPath(String id) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001914 enforcePermission(android.Manifest.permission.ASEC_ACCESS);
Dianne Hackborn292f8bc2011-06-27 16:27:41 -07001915 waitForReady();
1916 warnOnNotMounted();
1917
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001918 final NativeDaemonEvent event;
Dianne Hackborn292f8bc2011-06-27 16:27:41 -07001919 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001920 event = mConnector.execute("asec", "fspath", id);
1921 event.checkCode(VoldResponseCode.AsecPathResult);
1922 return event.getMessage();
Dianne Hackborn292f8bc2011-06-27 16:27:41 -07001923 } catch (NativeDaemonConnectorException e) {
1924 int code = e.getCode();
1925 if (code == VoldResponseCode.OpFailedStorageNotFound) {
1926 Slog.i(TAG, String.format("Container '%s' not found", id));
1927 return null;
1928 } else {
1929 throw new IllegalStateException(String.format("Unexpected response code %d", code));
1930 }
1931 }
1932 }
1933
Jeff Sharkey48877892015-03-18 11:27:19 -07001934 @Override
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -07001935 public void finishMediaUpdate() {
Rubin Xucd7a0142015-04-17 23:45:27 +01001936 if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
1937 throw new SecurityException("no permission to call finishMediaUpdate()");
1938 }
Jeff Sharkey48877892015-03-18 11:27:19 -07001939 if (mUnmountSignal != null) {
1940 mUnmountSignal.countDown();
1941 } else {
1942 Slog.w(TAG, "Odd, nobody asked to unmount?");
1943 }
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -07001944 }
Kenny Root02c87302010-07-01 08:10:18 -07001945
Kenny Roota02b8b02010-08-05 16:14:17 -07001946 private boolean isUidOwnerOfPackageOrSystem(String packageName, int callerUid) {
1947 if (callerUid == android.os.Process.SYSTEM_UID) {
1948 return true;
1949 }
1950
Kenny Root02c87302010-07-01 08:10:18 -07001951 if (packageName == null) {
1952 return false;
1953 }
1954
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07001955 final int packageUid = mPms.getPackageUid(packageName, UserHandle.getUserId(callerUid));
Kenny Root02c87302010-07-01 08:10:18 -07001956
1957 if (DEBUG_OBB) {
1958 Slog.d(TAG, "packageName = " + packageName + ", packageUid = " +
1959 packageUid + ", callerUid = " + callerUid);
1960 }
1961
1962 return callerUid == packageUid;
1963 }
1964
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001965 public String getMountedObbPath(String rawPath) {
1966 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
Kenny Rootaf9d6672010-10-08 09:21:39 -07001967
Kenny Root02c87302010-07-01 08:10:18 -07001968 waitForReady();
1969 warnOnNotMounted();
1970
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001971 final ObbState state;
Rubin Xucd7a0142015-04-17 23:45:27 +01001972 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001973 state = mObbPathToStateMap.get(rawPath);
1974 }
1975 if (state == null) {
1976 Slog.w(TAG, "Failed to find OBB mounted at " + rawPath);
1977 return null;
1978 }
1979
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001980 final NativeDaemonEvent event;
Kenny Root02c87302010-07-01 08:10:18 -07001981 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001982 event = mConnector.execute("obb", "path", state.voldPath);
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001983 event.checkCode(VoldResponseCode.AsecPathResult);
1984 return event.getMessage();
Kenny Root02c87302010-07-01 08:10:18 -07001985 } catch (NativeDaemonConnectorException e) {
1986 int code = e.getCode();
1987 if (code == VoldResponseCode.OpFailedStorageNotFound) {
Kenny Roota02b8b02010-08-05 16:14:17 -07001988 return null;
Kenny Root02c87302010-07-01 08:10:18 -07001989 } else {
1990 throw new IllegalStateException(String.format("Unexpected response code %d", code));
1991 }
1992 }
1993 }
1994
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001995 @Override
1996 public boolean isObbMounted(String rawPath) {
1997 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
Kenny Rootaf9d6672010-10-08 09:21:39 -07001998 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001999 return mObbPathToStateMap.containsKey(rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002000 }
Kenny Root02c87302010-07-01 08:10:18 -07002001 }
2002
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002003 @Override
2004 public void mountObb(
2005 String rawPath, String canonicalPath, String key, IObbActionListener token, int nonce) {
2006 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
2007 Preconditions.checkNotNull(canonicalPath, "canonicalPath cannot be null");
2008 Preconditions.checkNotNull(token, "token cannot be null");
Kenny Rootaf9d6672010-10-08 09:21:39 -07002009
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002010 final int callingUid = Binder.getCallingUid();
2011 final ObbState obbState = new ObbState(rawPath, canonicalPath, callingUid, token, nonce);
2012 final ObbAction action = new MountObbAction(obbState, key, callingUid);
Kenny Roota02b8b02010-08-05 16:14:17 -07002013 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
2014
2015 if (DEBUG_OBB)
2016 Slog.i(TAG, "Send to OBB handler: " + action.toString());
Kenny Root02c87302010-07-01 08:10:18 -07002017 }
2018
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002019 @Override
2020 public void unmountObb(String rawPath, boolean force, IObbActionListener token, int nonce) {
2021 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
2022
2023 final ObbState existingState;
Rubin Xucd7a0142015-04-17 23:45:27 +01002024 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002025 existingState = mObbPathToStateMap.get(rawPath);
Kenny Rootf1121dc2010-09-29 07:30:53 -07002026 }
2027
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002028 if (existingState != null) {
2029 // TODO: separate state object from request data
2030 final int callingUid = Binder.getCallingUid();
2031 final ObbState newState = new ObbState(
2032 rawPath, existingState.canonicalPath, callingUid, token, nonce);
2033 final ObbAction action = new UnmountObbAction(newState, force);
2034 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
Kenny Root02c87302010-07-01 08:10:18 -07002035
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002036 if (DEBUG_OBB)
2037 Slog.i(TAG, "Send to OBB handler: " + action.toString());
2038 } else {
2039 Slog.w(TAG, "Unknown OBB mount at " + rawPath);
2040 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002041 }
2042
Ben Komalo444eca22011-09-01 15:17:44 -07002043 @Override
2044 public int getEncryptionState() {
2045 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2046 "no permission to access the crypt keeper");
2047
2048 waitForReady();
2049
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002050 final NativeDaemonEvent event;
Ben Komalo444eca22011-09-01 15:17:44 -07002051 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002052 event = mConnector.execute("cryptfs", "cryptocomplete");
2053 return Integer.parseInt(event.getMessage());
Ben Komalo444eca22011-09-01 15:17:44 -07002054 } catch (NumberFormatException e) {
2055 // Bad result - unexpected.
2056 Slog.w(TAG, "Unable to parse result from cryptfs cryptocomplete");
2057 return ENCRYPTION_STATE_ERROR_UNKNOWN;
2058 } catch (NativeDaemonConnectorException e) {
2059 // Something bad happened.
2060 Slog.w(TAG, "Error in communicating with cryptfs in validating");
2061 return ENCRYPTION_STATE_ERROR_UNKNOWN;
2062 }
2063 }
2064
Narayan Kamath1653b1d2014-12-17 13:40:36 +00002065 private static String toHex(String password) {
Paul Lawrence8e397362014-01-27 15:22:30 -08002066 if (password == null) {
Narayan Kamath78108a32014-12-16 12:56:23 +00002067 return "";
Paul Lawrence8e397362014-01-27 15:22:30 -08002068 }
2069 byte[] bytes = password.getBytes(StandardCharsets.UTF_8);
Narayan Kamath78108a32014-12-16 12:56:23 +00002070 return new String(HexEncoding.encode(bytes));
Paul Lawrence8e397362014-01-27 15:22:30 -08002071 }
2072
Narayan Kamath1653b1d2014-12-17 13:40:36 +00002073 private static String fromHex(String hexPassword) throws IllegalArgumentException {
Paul Lawrence945490c2014-03-27 16:37:28 +00002074 if (hexPassword == null) {
2075 return null;
2076 }
2077
Narayan Kamath1653b1d2014-12-17 13:40:36 +00002078 final byte[] bytes = HexEncoding.decode(hexPassword.toCharArray(), false);
Narayan Kamath78108a32014-12-16 12:56:23 +00002079 return new String(bytes, StandardCharsets.UTF_8);
Paul Lawrence945490c2014-03-27 16:37:28 +00002080 }
2081
Ben Komalo444eca22011-09-01 15:17:44 -07002082 @Override
Jason parks5af0b912010-11-29 09:05:25 -06002083 public int decryptStorage(String password) {
Jason parksf7b3cd42011-01-27 09:28:25 -06002084 if (TextUtils.isEmpty(password)) {
2085 throw new IllegalArgumentException("password cannot be empty");
Jason parks5af0b912010-11-29 09:05:25 -06002086 }
2087
Jason parks8888c592011-01-20 22:46:41 -06002088 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2089 "no permission to access the crypt keeper");
Jason parks5af0b912010-11-29 09:05:25 -06002090
2091 waitForReady();
2092
2093 if (DEBUG_EVENTS) {
2094 Slog.i(TAG, "decrypting storage...");
2095 }
2096
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002097 final NativeDaemonEvent event;
Jason parks5af0b912010-11-29 09:05:25 -06002098 try {
Paul Lawrence8e397362014-01-27 15:22:30 -08002099 event = mConnector.execute("cryptfs", "checkpw", new SensitiveArg(toHex(password)));
Jason parks9ed98bc2011-01-17 09:58:35 -06002100
Fredrik Roubertda6aedf2011-12-20 17:34:43 +01002101 final int code = Integer.parseInt(event.getMessage());
Jason parks9ed98bc2011-01-17 09:58:35 -06002102 if (code == 0) {
2103 // Decrypt was successful. Post a delayed message before restarting in order
2104 // to let the UI to clear itself
2105 mHandler.postDelayed(new Runnable() {
2106 public void run() {
Jeff Sharkey31c6e482011-11-18 17:09:01 -08002107 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002108 mConnector.execute("cryptfs", "restart");
Jeff Sharkey31c6e482011-11-18 17:09:01 -08002109 } catch (NativeDaemonConnectorException e) {
2110 Slog.e(TAG, "problem executing in background", e);
2111 }
Jason parks9ed98bc2011-01-17 09:58:35 -06002112 }
Jason parksf7b3cd42011-01-27 09:28:25 -06002113 }, 1000); // 1 second
Jason parks9ed98bc2011-01-17 09:58:35 -06002114 }
2115
2116 return code;
Jason parks5af0b912010-11-29 09:05:25 -06002117 } catch (NativeDaemonConnectorException e) {
2118 // Decryption failed
2119 return e.getCode();
2120 }
Jason parks5af0b912010-11-29 09:05:25 -06002121 }
2122
Paul Lawrence46791e72014-04-03 09:10:26 -07002123 public int encryptStorage(int type, String password) {
2124 if (TextUtils.isEmpty(password) && type != StorageManager.CRYPT_TYPE_DEFAULT) {
Jason parksf7b3cd42011-01-27 09:28:25 -06002125 throw new IllegalArgumentException("password cannot be empty");
Jason parks56aa5322011-01-07 09:01:15 -06002126 }
2127
Jason parks8888c592011-01-20 22:46:41 -06002128 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2129 "no permission to access the crypt keeper");
Jason parks56aa5322011-01-07 09:01:15 -06002130
2131 waitForReady();
2132
2133 if (DEBUG_EVENTS) {
Jason parks8888c592011-01-20 22:46:41 -06002134 Slog.i(TAG, "encrypting storage...");
Jason parks56aa5322011-01-07 09:01:15 -06002135 }
2136
2137 try {
Paul Lawrence46791e72014-04-03 09:10:26 -07002138 mConnector.execute("cryptfs", "enablecrypto", "inplace", CRYPTO_TYPES[type],
Paul Lawrence8e397362014-01-27 15:22:30 -08002139 new SensitiveArg(toHex(password)));
Jason parks56aa5322011-01-07 09:01:15 -06002140 } catch (NativeDaemonConnectorException e) {
2141 // Encryption failed
2142 return e.getCode();
2143 }
2144
2145 return 0;
2146 }
2147
Paul Lawrence8e397362014-01-27 15:22:30 -08002148 /** Set the password for encrypting the master key.
2149 * @param type One of the CRYPTO_TYPE_XXX consts defined in StorageManager.
2150 * @param password The password to set.
2151 */
2152 public int changeEncryptionPassword(int type, String password) {
Jason parksf7b3cd42011-01-27 09:28:25 -06002153 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2154 "no permission to access the crypt keeper");
2155
2156 waitForReady();
2157
2158 if (DEBUG_EVENTS) {
2159 Slog.i(TAG, "changing encryption password...");
2160 }
2161
2162 try {
Svetoslava6711ff2014-10-17 11:38:06 -07002163 NativeDaemonEvent event = mConnector.execute("cryptfs", "changepw", CRYPTO_TYPES[type],
Svetoslav16e4a1a2014-09-29 18:16:20 -07002164 new SensitiveArg(toHex(password)));
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002165 return Integer.parseInt(event.getMessage());
Jason parksf7b3cd42011-01-27 09:28:25 -06002166 } catch (NativeDaemonConnectorException e) {
2167 // Encryption failed
2168 return e.getCode();
2169 }
2170 }
2171
Christopher Tate32418be2011-10-10 13:51:12 -07002172 /**
2173 * Validate a user-supplied password string with cryptfs
2174 */
2175 @Override
2176 public int verifyEncryptionPassword(String password) throws RemoteException {
2177 // Only the system process is permitted to validate passwords
2178 if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
2179 throw new SecurityException("no permission to access the crypt keeper");
2180 }
2181
2182 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2183 "no permission to access the crypt keeper");
2184
2185 if (TextUtils.isEmpty(password)) {
2186 throw new IllegalArgumentException("password cannot be empty");
2187 }
2188
2189 waitForReady();
2190
2191 if (DEBUG_EVENTS) {
2192 Slog.i(TAG, "validating encryption password...");
2193 }
2194
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002195 final NativeDaemonEvent event;
Christopher Tate32418be2011-10-10 13:51:12 -07002196 try {
Paul Lawrence8e397362014-01-27 15:22:30 -08002197 event = mConnector.execute("cryptfs", "verifypw", new SensitiveArg(toHex(password)));
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002198 Slog.i(TAG, "cryptfs verifypw => " + event.getMessage());
2199 return Integer.parseInt(event.getMessage());
Christopher Tate32418be2011-10-10 13:51:12 -07002200 } catch (NativeDaemonConnectorException e) {
2201 // Encryption failed
2202 return e.getCode();
2203 }
2204 }
2205
Paul Lawrence8e397362014-01-27 15:22:30 -08002206 /**
2207 * Get the type of encryption used to encrypt the master key.
2208 * @return The type, one of the CRYPT_TYPE_XXX consts from StorageManager.
2209 */
2210 @Override
Svetoslav16e4a1a2014-09-29 18:16:20 -07002211 public int getPasswordType() {
Paul Lawrence8e397362014-01-27 15:22:30 -08002212
2213 waitForReady();
2214
2215 final NativeDaemonEvent event;
2216 try {
2217 event = mConnector.execute("cryptfs", "getpwtype");
2218 for (int i = 0; i < CRYPTO_TYPES.length; ++i) {
2219 if (CRYPTO_TYPES[i].equals(event.getMessage()))
2220 return i;
2221 }
2222
2223 throw new IllegalStateException("unexpected return from cryptfs");
2224 } catch (NativeDaemonConnectorException e) {
2225 throw e.rethrowAsParcelableException();
2226 }
2227 }
2228
Paul Lawrencee51dcf92014-03-18 10:56:00 -07002229 /**
2230 * Set a field in the crypto header.
2231 * @param field field to set
2232 * @param contents contents to set in field
2233 */
2234 @Override
2235 public void setField(String field, String contents) throws RemoteException {
2236
2237 waitForReady();
2238
2239 final NativeDaemonEvent event;
2240 try {
2241 event = mConnector.execute("cryptfs", "setfield", field, contents);
2242 } catch (NativeDaemonConnectorException e) {
2243 throw e.rethrowAsParcelableException();
2244 }
2245 }
2246
2247 /**
2248 * Gets a field from the crypto header.
2249 * @param field field to get
2250 * @return contents of field
2251 */
2252 @Override
2253 public String getField(String field) throws RemoteException {
2254
2255 waitForReady();
2256
2257 final NativeDaemonEvent event;
2258 try {
2259 final String[] contents = NativeDaemonEvent.filterMessageList(
2260 mConnector.executeForList("cryptfs", "getfield", field),
2261 VoldResponseCode.CryptfsGetfieldResult);
2262 String result = new String();
2263 for (String content : contents) {
2264 result += content;
2265 }
2266 return result;
2267 } catch (NativeDaemonConnectorException e) {
2268 throw e.rethrowAsParcelableException();
2269 }
2270 }
2271
Jeff Sharkeyb049e212012-09-07 23:16:01 -07002272 @Override
Paul Lawrence945490c2014-03-27 16:37:28 +00002273 public String getPassword() throws RemoteException {
Rubin Xucd7a0142015-04-17 23:45:27 +01002274 mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE,
2275 "only keyguard can retrieve password");
Paul Lawrence945490c2014-03-27 16:37:28 +00002276 if (!isReady()) {
2277 return new String();
2278 }
2279
2280 final NativeDaemonEvent event;
2281 try {
2282 event = mConnector.execute("cryptfs", "getpw");
Paul Lawrence24063b52015-01-06 13:11:23 -08002283 if ("-1".equals(event.getMessage())) {
2284 // -1 equals no password
2285 return null;
2286 }
Paul Lawrence945490c2014-03-27 16:37:28 +00002287 return fromHex(event.getMessage());
2288 } catch (NativeDaemonConnectorException e) {
2289 throw e.rethrowAsParcelableException();
Paul Lawrence24063b52015-01-06 13:11:23 -08002290 } catch (IllegalArgumentException e) {
2291 Slog.e(TAG, "Invalid response to getPassword");
2292 return null;
Paul Lawrence945490c2014-03-27 16:37:28 +00002293 }
2294 }
2295
2296 @Override
2297 public void clearPassword() throws RemoteException {
2298 if (!isReady()) {
2299 return;
2300 }
2301
2302 final NativeDaemonEvent event;
2303 try {
2304 event = mConnector.execute("cryptfs", "clearpw");
2305 } catch (NativeDaemonConnectorException e) {
2306 throw e.rethrowAsParcelableException();
2307 }
2308 }
2309
2310 @Override
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002311 public int mkdirs(String callingPkg, String appPath) {
2312 final int userId = UserHandle.getUserId(Binder.getCallingUid());
2313 final UserEnvironment userEnv = new UserEnvironment(userId);
2314
2315 // Validate that reported package name belongs to caller
2316 final AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(
2317 Context.APP_OPS_SERVICE);
2318 appOps.checkPackage(Binder.getCallingUid(), callingPkg);
2319
Jeff Sharkey48877892015-03-18 11:27:19 -07002320 File appFile = null;
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002321 try {
Jeff Sharkey48877892015-03-18 11:27:19 -07002322 appFile = new File(appPath).getCanonicalFile();
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002323 } catch (IOException e) {
2324 Slog.e(TAG, "Failed to resolve " + appPath + ": " + e);
2325 return -1;
2326 }
2327
2328 // Try translating the app path into a vold path, but require that it
2329 // belong to the calling package.
Jeff Sharkey48877892015-03-18 11:27:19 -07002330 if (FileUtils.contains(userEnv.buildExternalStorageAppDataDirs(callingPkg), appFile) ||
2331 FileUtils.contains(userEnv.buildExternalStorageAppObbDirs(callingPkg), appFile) ||
2332 FileUtils.contains(userEnv.buildExternalStorageAppMediaDirs(callingPkg), appFile)) {
2333 appPath = appFile.getAbsolutePath();
2334 if (!appPath.endsWith("/")) {
2335 appPath = appPath + "/";
2336 }
2337
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002338 try {
Jeff Sharkey48877892015-03-18 11:27:19 -07002339 mConnector.execute("volume", "mkdirs", appPath);
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002340 return 0;
2341 } catch (NativeDaemonConnectorException e) {
2342 return e.getCode();
2343 }
2344 }
2345
Jeff Sharkey48877892015-03-18 11:27:19 -07002346 throw new SecurityException("Invalid mkdirs path: " + appFile);
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002347 }
2348
2349 @Override
Jeff Sharkey48877892015-03-18 11:27:19 -07002350 public StorageVolume[] getVolumeList(int userId) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002351 final ArrayList<StorageVolume> res = new ArrayList<>();
Jeff Sharkey48877892015-03-18 11:27:19 -07002352 boolean foundPrimary = false;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002353
Jeff Sharkey48877892015-03-18 11:27:19 -07002354 synchronized (mLock) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002355 for (int i = 0; i < mVolumes.size(); i++) {
2356 final VolumeInfo vol = mVolumes.valueAt(i);
Jeff Sharkey48877892015-03-18 11:27:19 -07002357 if (vol.isVisibleToUser(userId)) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002358 final StorageVolume userVol = vol.buildStorageVolume(mContext, userId);
Jeff Sharkey48877892015-03-18 11:27:19 -07002359 if (vol.isPrimary()) {
2360 res.add(0, userVol);
2361 foundPrimary = true;
2362 } else {
2363 res.add(userVol);
2364 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -07002365 }
Mike Lockwood8fa5f802011-03-24 08:12:30 -07002366 }
Mike Lockwood8fa5f802011-03-24 08:12:30 -07002367 }
Jeff Sharkey48877892015-03-18 11:27:19 -07002368
2369 if (!foundPrimary) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002370 Log.w(TAG, "No primary storage defined yet; hacking together a stub");
Jeff Sharkey48877892015-03-18 11:27:19 -07002371
2372 final boolean primaryPhysical = SystemProperties.getBoolean(
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002373 StorageManager.PROP_PRIMARY_PHYSICAL, false);
Jeff Sharkey48877892015-03-18 11:27:19 -07002374
2375 final String id = "stub_primary";
2376 final File path = Environment.getLegacyExternalStorageDirectory();
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002377 final String description = mContext.getString(android.R.string.unknownName);
Jeff Sharkey48877892015-03-18 11:27:19 -07002378 final boolean primary = true;
2379 final boolean removable = primaryPhysical;
2380 final boolean emulated = !primaryPhysical;
2381 final long mtpReserveSize = 0L;
2382 final boolean allowMassStorage = false;
2383 final long maxFileSize = 0L;
2384 final UserHandle owner = new UserHandle(userId);
2385 final String uuid = null;
Jeff Sharkey48877892015-03-18 11:27:19 -07002386 final String state = Environment.MEDIA_REMOVED;
2387
2388 res.add(0, new StorageVolume(id, MtpStorage.getStorageIdForIndex(0), path,
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002389 description, primary, removable, emulated, mtpReserveSize,
2390 allowMassStorage, maxFileSize, owner, uuid, state));
Jeff Sharkey48877892015-03-18 11:27:19 -07002391 }
2392
2393 return res.toArray(new StorageVolume[res.size()]);
Mike Lockwood8fa5f802011-03-24 08:12:30 -07002394 }
2395
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002396 @Override
2397 public DiskInfo[] getDisks() {
2398 synchronized (mLock) {
2399 final DiskInfo[] res = new DiskInfo[mDisks.size()];
2400 for (int i = 0; i < mDisks.size(); i++) {
2401 res[i] = mDisks.valueAt(i);
2402 }
2403 return res;
2404 }
2405 }
2406
2407 @Override
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07002408 public VolumeInfo[] getVolumes(int flags) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002409 synchronized (mLock) {
2410 final VolumeInfo[] res = new VolumeInfo[mVolumes.size()];
2411 for (int i = 0; i < mVolumes.size(); i++) {
2412 res[i] = mVolumes.valueAt(i);
2413 }
2414 return res;
2415 }
2416 }
2417
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07002418 @Override
2419 public VolumeRecord[] getVolumeRecords(int flags) {
2420 synchronized (mLock) {
2421 final VolumeRecord[] res = new VolumeRecord[mRecords.size()];
2422 for (int i = 0; i < mRecords.size(); i++) {
2423 res[i] = mRecords.valueAt(i);
2424 }
2425 return res;
2426 }
2427 }
2428
Kenny Rootaf9d6672010-10-08 09:21:39 -07002429 private void addObbStateLocked(ObbState obbState) throws RemoteException {
2430 final IBinder binder = obbState.getBinder();
2431 List<ObbState> obbStates = mObbMounts.get(binder);
Kenny Root5919ac62010-10-05 09:49:40 -07002432
Kenny Rootaf9d6672010-10-08 09:21:39 -07002433 if (obbStates == null) {
2434 obbStates = new ArrayList<ObbState>();
2435 mObbMounts.put(binder, obbStates);
2436 } else {
2437 for (final ObbState o : obbStates) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002438 if (o.rawPath.equals(obbState.rawPath)) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002439 throw new IllegalStateException("Attempt to add ObbState twice. "
2440 + "This indicates an error in the MountService logic.");
Kenny Root5919ac62010-10-05 09:49:40 -07002441 }
2442 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002443 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07002444
2445 obbStates.add(obbState);
2446 try {
2447 obbState.link();
2448 } catch (RemoteException e) {
2449 /*
2450 * The binder died before we could link it, so clean up our state
2451 * and return failure.
2452 */
2453 obbStates.remove(obbState);
2454 if (obbStates.isEmpty()) {
2455 mObbMounts.remove(binder);
2456 }
2457
2458 // Rethrow the error so mountObb can get it
2459 throw e;
2460 }
2461
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002462 mObbPathToStateMap.put(obbState.rawPath, obbState);
Kenny Roota02b8b02010-08-05 16:14:17 -07002463 }
2464
Kenny Rootaf9d6672010-10-08 09:21:39 -07002465 private void removeObbStateLocked(ObbState obbState) {
2466 final IBinder binder = obbState.getBinder();
2467 final List<ObbState> obbStates = mObbMounts.get(binder);
2468 if (obbStates != null) {
2469 if (obbStates.remove(obbState)) {
2470 obbState.unlink();
Kenny Root05105f72010-09-22 17:29:43 -07002471 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07002472 if (obbStates.isEmpty()) {
2473 mObbMounts.remove(binder);
2474 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002475 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002476
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002477 mObbPathToStateMap.remove(obbState.rawPath);
Kenny Root38cf8862010-09-26 14:18:51 -07002478 }
2479
Kenny Roota02b8b02010-08-05 16:14:17 -07002480 private class ObbActionHandler extends Handler {
2481 private boolean mBound = false;
Kenny Root480afe72010-10-07 10:17:50 -07002482 private final List<ObbAction> mActions = new LinkedList<ObbAction>();
Kenny Roota02b8b02010-08-05 16:14:17 -07002483
2484 ObbActionHandler(Looper l) {
2485 super(l);
2486 }
2487
2488 @Override
2489 public void handleMessage(Message msg) {
2490 switch (msg.what) {
2491 case OBB_RUN_ACTION: {
Kenny Root480afe72010-10-07 10:17:50 -07002492 final ObbAction action = (ObbAction) msg.obj;
Kenny Roota02b8b02010-08-05 16:14:17 -07002493
2494 if (DEBUG_OBB)
2495 Slog.i(TAG, "OBB_RUN_ACTION: " + action.toString());
2496
2497 // If a bind was already initiated we don't really
2498 // need to do anything. The pending install
2499 // will be processed later on.
2500 if (!mBound) {
2501 // If this is the only one pending we might
2502 // have to bind to the service again.
2503 if (!connectToService()) {
2504 Slog.e(TAG, "Failed to bind to media container service");
2505 action.handleError();
2506 return;
Kenny Roota02b8b02010-08-05 16:14:17 -07002507 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002508 }
Kenny Root735de3b2010-09-30 14:11:39 -07002509
Kenny Root735de3b2010-09-30 14:11:39 -07002510 mActions.add(action);
Kenny Roota02b8b02010-08-05 16:14:17 -07002511 break;
2512 }
2513 case OBB_MCS_BOUND: {
2514 if (DEBUG_OBB)
2515 Slog.i(TAG, "OBB_MCS_BOUND");
2516 if (msg.obj != null) {
2517 mContainerService = (IMediaContainerService) msg.obj;
2518 }
2519 if (mContainerService == null) {
2520 // Something seriously wrong. Bail out
2521 Slog.e(TAG, "Cannot bind to media container service");
2522 for (ObbAction action : mActions) {
2523 // Indicate service bind error
2524 action.handleError();
2525 }
2526 mActions.clear();
2527 } else if (mActions.size() > 0) {
Kenny Root480afe72010-10-07 10:17:50 -07002528 final ObbAction action = mActions.get(0);
Kenny Roota02b8b02010-08-05 16:14:17 -07002529 if (action != null) {
2530 action.execute(this);
2531 }
2532 } else {
2533 // Should never happen ideally.
2534 Slog.w(TAG, "Empty queue");
2535 }
2536 break;
2537 }
2538 case OBB_MCS_RECONNECT: {
2539 if (DEBUG_OBB)
2540 Slog.i(TAG, "OBB_MCS_RECONNECT");
2541 if (mActions.size() > 0) {
2542 if (mBound) {
2543 disconnectService();
2544 }
2545 if (!connectToService()) {
2546 Slog.e(TAG, "Failed to bind to media container service");
2547 for (ObbAction action : mActions) {
2548 // Indicate service bind error
2549 action.handleError();
2550 }
2551 mActions.clear();
2552 }
2553 }
2554 break;
2555 }
2556 case OBB_MCS_UNBIND: {
2557 if (DEBUG_OBB)
2558 Slog.i(TAG, "OBB_MCS_UNBIND");
2559
2560 // Delete pending install
2561 if (mActions.size() > 0) {
2562 mActions.remove(0);
2563 }
2564 if (mActions.size() == 0) {
2565 if (mBound) {
2566 disconnectService();
2567 }
2568 } else {
2569 // There are more pending requests in queue.
2570 // Just post MCS_BOUND message to trigger processing
2571 // of next pending install.
2572 mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND);
2573 }
2574 break;
2575 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07002576 case OBB_FLUSH_MOUNT_STATE: {
2577 final String path = (String) msg.obj;
2578
2579 if (DEBUG_OBB)
2580 Slog.i(TAG, "Flushing all OBB state for path " + path);
2581
2582 synchronized (mObbMounts) {
2583 final List<ObbState> obbStatesToRemove = new LinkedList<ObbState>();
2584
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002585 final Iterator<ObbState> i = mObbPathToStateMap.values().iterator();
Kenny Rootaf9d6672010-10-08 09:21:39 -07002586 while (i.hasNext()) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002587 final ObbState state = i.next();
Kenny Rootaf9d6672010-10-08 09:21:39 -07002588
2589 /*
2590 * If this entry's source file is in the volume path
2591 * that got unmounted, remove it because it's no
2592 * longer valid.
2593 */
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002594 if (state.canonicalPath.startsWith(path)) {
2595 obbStatesToRemove.add(state);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002596 }
2597 }
2598
2599 for (final ObbState obbState : obbStatesToRemove) {
2600 if (DEBUG_OBB)
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002601 Slog.i(TAG, "Removing state for " + obbState.rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002602
2603 removeObbStateLocked(obbState);
2604
2605 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002606 obbState.token.onObbResult(obbState.rawPath, obbState.nonce,
Kenny Rootaf9d6672010-10-08 09:21:39 -07002607 OnObbStateChangeListener.UNMOUNTED);
2608 } catch (RemoteException e) {
2609 Slog.i(TAG, "Couldn't send unmount notification for OBB: "
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002610 + obbState.rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002611 }
2612 }
2613 }
2614 break;
2615 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002616 }
2617 }
2618
2619 private boolean connectToService() {
2620 if (DEBUG_OBB)
2621 Slog.i(TAG, "Trying to bind to DefaultContainerService");
2622
2623 Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
2624 if (mContext.bindService(service, mDefContainerConn, Context.BIND_AUTO_CREATE)) {
2625 mBound = true;
2626 return true;
2627 }
2628 return false;
2629 }
2630
2631 private void disconnectService() {
2632 mContainerService = null;
2633 mBound = false;
2634 mContext.unbindService(mDefContainerConn);
2635 }
2636 }
2637
2638 abstract class ObbAction {
2639 private static final int MAX_RETRIES = 3;
2640 private int mRetries;
2641
2642 ObbState mObbState;
2643
2644 ObbAction(ObbState obbState) {
2645 mObbState = obbState;
2646 }
2647
2648 public void execute(ObbActionHandler handler) {
2649 try {
2650 if (DEBUG_OBB)
Ben Komalo444eca22011-09-01 15:17:44 -07002651 Slog.i(TAG, "Starting to execute action: " + toString());
Kenny Roota02b8b02010-08-05 16:14:17 -07002652 mRetries++;
2653 if (mRetries > MAX_RETRIES) {
2654 Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
Kenny Root480afe72010-10-07 10:17:50 -07002655 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
Kenny Roota02b8b02010-08-05 16:14:17 -07002656 handleError();
2657 return;
2658 } else {
2659 handleExecute();
2660 if (DEBUG_OBB)
2661 Slog.i(TAG, "Posting install MCS_UNBIND");
2662 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
2663 }
2664 } catch (RemoteException e) {
2665 if (DEBUG_OBB)
2666 Slog.i(TAG, "Posting install MCS_RECONNECT");
2667 mObbActionHandler.sendEmptyMessage(OBB_MCS_RECONNECT);
2668 } catch (Exception e) {
2669 if (DEBUG_OBB)
2670 Slog.d(TAG, "Error handling OBB action", e);
2671 handleError();
Kenny Root17eb6fb2010-10-06 15:02:52 -07002672 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
Kenny Roota02b8b02010-08-05 16:14:17 -07002673 }
2674 }
2675
Kenny Root05105f72010-09-22 17:29:43 -07002676 abstract void handleExecute() throws RemoteException, IOException;
Kenny Roota02b8b02010-08-05 16:14:17 -07002677 abstract void handleError();
Kenny Root38cf8862010-09-26 14:18:51 -07002678
2679 protected ObbInfo getObbInfo() throws IOException {
2680 ObbInfo obbInfo;
2681 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002682 obbInfo = mContainerService.getObbInfo(mObbState.ownerPath);
Kenny Root38cf8862010-09-26 14:18:51 -07002683 } catch (RemoteException e) {
2684 Slog.d(TAG, "Couldn't call DefaultContainerService to fetch OBB info for "
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002685 + mObbState.ownerPath);
Kenny Root38cf8862010-09-26 14:18:51 -07002686 obbInfo = null;
2687 }
2688 if (obbInfo == null) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002689 throw new IOException("Couldn't read OBB file: " + mObbState.ownerPath);
Kenny Root38cf8862010-09-26 14:18:51 -07002690 }
2691 return obbInfo;
2692 }
2693
Kenny Rootaf9d6672010-10-08 09:21:39 -07002694 protected void sendNewStatusOrIgnore(int status) {
2695 if (mObbState == null || mObbState.token == null) {
2696 return;
2697 }
2698
Kenny Root38cf8862010-09-26 14:18:51 -07002699 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002700 mObbState.token.onObbResult(mObbState.rawPath, mObbState.nonce, status);
Kenny Root38cf8862010-09-26 14:18:51 -07002701 } catch (RemoteException e) {
2702 Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
2703 }
2704 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002705 }
2706
2707 class MountObbAction extends ObbAction {
Ben Komalo444eca22011-09-01 15:17:44 -07002708 private final String mKey;
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002709 private final int mCallingUid;
Kenny Roota02b8b02010-08-05 16:14:17 -07002710
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002711 MountObbAction(ObbState obbState, String key, int callingUid) {
Kenny Roota02b8b02010-08-05 16:14:17 -07002712 super(obbState);
2713 mKey = key;
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002714 mCallingUid = callingUid;
Kenny Roota02b8b02010-08-05 16:14:17 -07002715 }
2716
Jason parks5af0b912010-11-29 09:05:25 -06002717 @Override
Kenny Root735de3b2010-09-30 14:11:39 -07002718 public void handleExecute() throws IOException, RemoteException {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002719 waitForReady();
2720 warnOnNotMounted();
2721
Kenny Root38cf8862010-09-26 14:18:51 -07002722 final ObbInfo obbInfo = getObbInfo();
2723
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002724 if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mCallingUid)) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002725 Slog.w(TAG, "Denied attempt to mount OBB " + obbInfo.filename
2726 + " which is owned by " + obbInfo.packageName);
2727 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
2728 return;
Kenny Roota02b8b02010-08-05 16:14:17 -07002729 }
2730
Kenny Rootaf9d6672010-10-08 09:21:39 -07002731 final boolean isMounted;
2732 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002733 isMounted = mObbPathToStateMap.containsKey(mObbState.rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002734 }
2735 if (isMounted) {
2736 Slog.w(TAG, "Attempt to mount OBB which is already mounted: " + obbInfo.filename);
2737 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_ALREADY_MOUNTED);
2738 return;
2739 }
2740
Kenny Rootaf9d6672010-10-08 09:21:39 -07002741 final String hashedKey;
2742 if (mKey == null) {
2743 hashedKey = "none";
2744 } else {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002745 try {
Kenny Root3b1abba2010-10-13 15:00:07 -07002746 SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
2747
2748 KeySpec ks = new PBEKeySpec(mKey.toCharArray(), obbInfo.salt,
2749 PBKDF2_HASH_ROUNDS, CRYPTO_ALGORITHM_KEY_SIZE);
2750 SecretKey key = factory.generateSecret(ks);
2751 BigInteger bi = new BigInteger(key.getEncoded());
2752 hashedKey = bi.toString(16);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002753 } catch (NoSuchAlgorithmException e) {
Kenny Root3b1abba2010-10-13 15:00:07 -07002754 Slog.e(TAG, "Could not load PBKDF2 algorithm", e);
2755 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
2756 return;
2757 } catch (InvalidKeySpecException e) {
2758 Slog.e(TAG, "Invalid key spec when loading PBKDF2 algorithm", e);
2759 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
Kenny Root38cf8862010-09-26 14:18:51 -07002760 return;
2761 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07002762 }
Kenny Root38cf8862010-09-26 14:18:51 -07002763
Kenny Rootaf9d6672010-10-08 09:21:39 -07002764 int rc = StorageResultCode.OperationSucceeded;
Kenny Rootaf9d6672010-10-08 09:21:39 -07002765 try {
Jeff Sharkey56cd6462013-06-07 15:09:15 -07002766 mConnector.execute("obb", "mount", mObbState.voldPath, new SensitiveArg(hashedKey),
2767 mObbState.ownerGid);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002768 } catch (NativeDaemonConnectorException e) {
2769 int code = e.getCode();
2770 if (code != VoldResponseCode.OpFailedStorageBusy) {
2771 rc = StorageResultCode.OperationFailedInternalError;
Kenny Roota02b8b02010-08-05 16:14:17 -07002772 }
2773 }
2774
Kenny Rootaf9d6672010-10-08 09:21:39 -07002775 if (rc == StorageResultCode.OperationSucceeded) {
2776 if (DEBUG_OBB)
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002777 Slog.d(TAG, "Successfully mounted OBB " + mObbState.voldPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002778
2779 synchronized (mObbMounts) {
2780 addObbStateLocked(mObbState);
2781 }
2782
2783 sendNewStatusOrIgnore(OnObbStateChangeListener.MOUNTED);
Kenny Root02c87302010-07-01 08:10:18 -07002784 } else {
Kenny Root05105f72010-09-22 17:29:43 -07002785 Slog.e(TAG, "Couldn't mount OBB file: " + rc);
Kenny Roota02b8b02010-08-05 16:14:17 -07002786
Kenny Rootaf9d6672010-10-08 09:21:39 -07002787 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT);
Kenny Root02c87302010-07-01 08:10:18 -07002788 }
2789 }
2790
Jason parks5af0b912010-11-29 09:05:25 -06002791 @Override
Kenny Roota02b8b02010-08-05 16:14:17 -07002792 public void handleError() {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002793 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
Kenny Root02c87302010-07-01 08:10:18 -07002794 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002795
2796 @Override
2797 public String toString() {
2798 StringBuilder sb = new StringBuilder();
2799 sb.append("MountObbAction{");
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002800 sb.append(mObbState);
Kenny Roota02b8b02010-08-05 16:14:17 -07002801 sb.append('}');
2802 return sb.toString();
2803 }
2804 }
2805
2806 class UnmountObbAction extends ObbAction {
Ben Komalo444eca22011-09-01 15:17:44 -07002807 private final boolean mForceUnmount;
Kenny Roota02b8b02010-08-05 16:14:17 -07002808
2809 UnmountObbAction(ObbState obbState, boolean force) {
2810 super(obbState);
2811 mForceUnmount = force;
2812 }
2813
Jason parks5af0b912010-11-29 09:05:25 -06002814 @Override
Kenny Root38cf8862010-09-26 14:18:51 -07002815 public void handleExecute() throws IOException {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002816 waitForReady();
2817 warnOnNotMounted();
2818
Kenny Root38cf8862010-09-26 14:18:51 -07002819 final ObbInfo obbInfo = getObbInfo();
Kenny Roota02b8b02010-08-05 16:14:17 -07002820
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002821 final ObbState existingState;
Kenny Root38cf8862010-09-26 14:18:51 -07002822 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002823 existingState = mObbPathToStateMap.get(mObbState.rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002824 }
Kenny Root38cf8862010-09-26 14:18:51 -07002825
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002826 if (existingState == null) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002827 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_NOT_MOUNTED);
2828 return;
2829 }
2830
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002831 if (existingState.ownerGid != mObbState.ownerGid) {
2832 Slog.w(TAG, "Permission denied attempting to unmount OBB " + existingState.rawPath
2833 + " (owned by GID " + existingState.ownerGid + ")");
Kenny Rootaf9d6672010-10-08 09:21:39 -07002834 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
2835 return;
2836 }
2837
Kenny Rootaf9d6672010-10-08 09:21:39 -07002838 int rc = StorageResultCode.OperationSucceeded;
Kenny Rootaf9d6672010-10-08 09:21:39 -07002839 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002840 final Command cmd = new Command("obb", "unmount", mObbState.voldPath);
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002841 if (mForceUnmount) {
2842 cmd.appendArg("force");
2843 }
2844 mConnector.execute(cmd);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002845 } catch (NativeDaemonConnectorException e) {
2846 int code = e.getCode();
2847 if (code == VoldResponseCode.OpFailedStorageBusy) {
2848 rc = StorageResultCode.OperationFailedStorageBusy;
2849 } else if (code == VoldResponseCode.OpFailedStorageNotFound) {
2850 // If it's not mounted then we've already won.
2851 rc = StorageResultCode.OperationSucceeded;
2852 } else {
2853 rc = StorageResultCode.OperationFailedInternalError;
Kenny Roota02b8b02010-08-05 16:14:17 -07002854 }
2855 }
2856
Kenny Rootaf9d6672010-10-08 09:21:39 -07002857 if (rc == StorageResultCode.OperationSucceeded) {
2858 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002859 removeObbStateLocked(existingState);
Kenny Root38cf8862010-09-26 14:18:51 -07002860 }
2861
Kenny Rootaf9d6672010-10-08 09:21:39 -07002862 sendNewStatusOrIgnore(OnObbStateChangeListener.UNMOUNTED);
Kenny Roota02b8b02010-08-05 16:14:17 -07002863 } else {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002864 Slog.w(TAG, "Could not unmount OBB: " + existingState);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002865 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_UNMOUNT);
Kenny Roota02b8b02010-08-05 16:14:17 -07002866 }
2867 }
2868
Jason parks5af0b912010-11-29 09:05:25 -06002869 @Override
Kenny Roota02b8b02010-08-05 16:14:17 -07002870 public void handleError() {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002871 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
Kenny Roota02b8b02010-08-05 16:14:17 -07002872 }
2873
2874 @Override
2875 public String toString() {
2876 StringBuilder sb = new StringBuilder();
2877 sb.append("UnmountObbAction{");
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002878 sb.append(mObbState);
Kenny Roota02b8b02010-08-05 16:14:17 -07002879 sb.append(",force=");
2880 sb.append(mForceUnmount);
Kenny Roota02b8b02010-08-05 16:14:17 -07002881 sb.append('}');
2882 return sb.toString();
2883 }
Kenny Root02c87302010-07-01 08:10:18 -07002884 }
Kenny Root38cf8862010-09-26 14:18:51 -07002885
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -08002886 @VisibleForTesting
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002887 public static String buildObbPath(final String canonicalPath, int userId, boolean forVold) {
2888 // TODO: allow caller to provide Environment for full testing
Jeff Sharkey1abdb712013-08-11 16:28:14 -07002889 // TODO: extend to support OBB mounts on secondary external storage
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002890
2891 // Only adjust paths when storage is emulated
2892 if (!Environment.isExternalStorageEmulated()) {
2893 return canonicalPath;
2894 }
2895
2896 String path = canonicalPath.toString();
2897
2898 // First trim off any external storage prefix
2899 final UserEnvironment userEnv = new UserEnvironment(userId);
2900
2901 // /storage/emulated/0
Jeff Sharkey1abdb712013-08-11 16:28:14 -07002902 final String externalPath = userEnv.getExternalStorageDirectory().getAbsolutePath();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002903 // /storage/emulated_legacy
2904 final String legacyExternalPath = Environment.getLegacyExternalStorageDirectory()
Jeff Sharkey1abdb712013-08-11 16:28:14 -07002905 .getAbsolutePath();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002906
2907 if (path.startsWith(externalPath)) {
2908 path = path.substring(externalPath.length() + 1);
2909 } else if (path.startsWith(legacyExternalPath)) {
2910 path = path.substring(legacyExternalPath.length() + 1);
2911 } else {
2912 return canonicalPath;
2913 }
2914
2915 // Handle special OBB paths on emulated storage
2916 final String obbPath = "Android/obb";
2917 if (path.startsWith(obbPath)) {
2918 path = path.substring(obbPath.length() + 1);
2919
Jeff Sharkey48877892015-03-18 11:27:19 -07002920 final UserEnvironment ownerEnv = new UserEnvironment(UserHandle.USER_OWNER);
2921 return new File(ownerEnv.buildExternalStorageAndroidObbDirs()[0], path)
2922 .getAbsolutePath();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002923 }
2924
2925 // Handle normal external storage paths
Jeff Sharkey48877892015-03-18 11:27:19 -07002926 return new File(userEnv.getExternalStorageDirectory(), path).getAbsolutePath();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002927 }
2928
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07002929 private static class Callbacks extends Handler {
2930 private static final int MSG_STORAGE_STATE_CHANGED = 1;
2931 private static final int MSG_VOLUME_STATE_CHANGED = 2;
Jeff Sharkey50a05452015-04-29 11:24:52 -07002932 private static final int MSG_VOLUME_RECORD_CHANGED = 3;
2933 private static final int MSG_VOLUME_FORGOTTEN = 4;
2934 private static final int MSG_DISK_SCANNED = 5;
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07002935
2936 private final RemoteCallbackList<IMountServiceListener>
2937 mCallbacks = new RemoteCallbackList<>();
2938
2939 public Callbacks(Looper looper) {
2940 super(looper);
2941 }
2942
2943 public void register(IMountServiceListener callback) {
2944 mCallbacks.register(callback);
2945 }
2946
2947 public void unregister(IMountServiceListener callback) {
2948 mCallbacks.unregister(callback);
2949 }
2950
2951 @Override
2952 public void handleMessage(Message msg) {
2953 final SomeArgs args = (SomeArgs) msg.obj;
2954 final int n = mCallbacks.beginBroadcast();
2955 for (int i = 0; i < n; i++) {
2956 final IMountServiceListener callback = mCallbacks.getBroadcastItem(i);
2957 try {
2958 invokeCallback(callback, msg.what, args);
2959 } catch (RemoteException ignored) {
2960 }
2961 }
2962 mCallbacks.finishBroadcast();
2963 args.recycle();
2964 }
2965
2966 private void invokeCallback(IMountServiceListener callback, int what, SomeArgs args)
2967 throws RemoteException {
2968 switch (what) {
2969 case MSG_STORAGE_STATE_CHANGED: {
2970 callback.onStorageStateChanged((String) args.arg1, (String) args.arg2,
2971 (String) args.arg3);
2972 break;
2973 }
2974 case MSG_VOLUME_STATE_CHANGED: {
2975 callback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3);
2976 break;
2977 }
Jeff Sharkey50a05452015-04-29 11:24:52 -07002978 case MSG_VOLUME_RECORD_CHANGED: {
2979 callback.onVolumeRecordChanged((VolumeRecord) args.arg1);
2980 break;
2981 }
2982 case MSG_VOLUME_FORGOTTEN: {
2983 callback.onVolumeForgotten((String) args.arg1);
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07002984 break;
2985 }
Jeff Sharkey620b32b2015-04-23 19:36:02 -07002986 case MSG_DISK_SCANNED: {
2987 callback.onDiskScanned((DiskInfo) args.arg1, args.argi2);
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07002988 break;
2989 }
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07002990 }
2991 }
2992
2993 private void notifyStorageStateChanged(String path, String oldState, String newState) {
2994 final SomeArgs args = SomeArgs.obtain();
2995 args.arg1 = path;
2996 args.arg2 = oldState;
2997 args.arg3 = newState;
2998 obtainMessage(MSG_STORAGE_STATE_CHANGED, args).sendToTarget();
2999 }
3000
3001 private void notifyVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
3002 final SomeArgs args = SomeArgs.obtain();
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07003003 args.arg1 = vol.clone();
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07003004 args.argi2 = oldState;
3005 args.argi3 = newState;
3006 obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget();
3007 }
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07003008
Jeff Sharkey50a05452015-04-29 11:24:52 -07003009 private void notifyVolumeRecordChanged(VolumeRecord rec) {
3010 final SomeArgs args = SomeArgs.obtain();
3011 args.arg1 = rec.clone();
3012 obtainMessage(MSG_VOLUME_RECORD_CHANGED, args).sendToTarget();
3013 }
3014
3015 private void notifyVolumeForgotten(String fsUuid) {
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07003016 final SomeArgs args = SomeArgs.obtain();
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07003017 args.arg1 = fsUuid;
Jeff Sharkey50a05452015-04-29 11:24:52 -07003018 obtainMessage(MSG_VOLUME_FORGOTTEN, args).sendToTarget();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07003019 }
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07003020
Jeff Sharkey620b32b2015-04-23 19:36:02 -07003021 private void notifyDiskScanned(DiskInfo disk, int volumeCount) {
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07003022 final SomeArgs args = SomeArgs.obtain();
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07003023 args.arg1 = disk.clone();
Jeff Sharkey620b32b2015-04-23 19:36:02 -07003024 args.argi2 = volumeCount;
3025 obtainMessage(MSG_DISK_SCANNED, args).sendToTarget();
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07003026 }
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07003027 }
3028
Kenny Root38cf8862010-09-26 14:18:51 -07003029 @Override
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003030 protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
3031 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
3032
3033 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ", 160);
Jeff Sharkey27de30d2015-04-18 16:20:27 -07003034 synchronized (mLock) {
3035 pw.println("Disks:");
3036 pw.increaseIndent();
3037 for (int i = 0; i < mDisks.size(); i++) {
3038 final DiskInfo disk = mDisks.valueAt(i);
3039 disk.dump(pw);
3040 }
3041 pw.decreaseIndent();
3042
3043 pw.println();
3044 pw.println("Volumes:");
3045 pw.increaseIndent();
3046 for (int i = 0; i < mVolumes.size(); i++) {
3047 final VolumeInfo vol = mVolumes.valueAt(i);
3048 if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.id)) continue;
3049 vol.dump(pw);
3050 }
3051 pw.decreaseIndent();
3052
3053 pw.println();
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07003054 pw.println("Records:");
Jeff Sharkey27de30d2015-04-18 16:20:27 -07003055 pw.increaseIndent();
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07003056 for (int i = 0; i < mRecords.size(); i++) {
3057 final VolumeRecord note = mRecords.valueAt(i);
3058 note.dump(pw);
Jeff Sharkey27de30d2015-04-18 16:20:27 -07003059 }
3060 pw.decreaseIndent();
Jeff Sharkey275e3e42015-04-24 16:10:32 -07003061
3062 pw.println();
3063 pw.println("Primary storage UUID: " + mPrimaryStorageUuid);
Jeff Sharkey4c099d02015-05-15 13:45:00 -07003064 pw.println("Force adoptable: " + mForceAdoptable);
Jeff Sharkey27de30d2015-04-18 16:20:27 -07003065 }
Kenny Root38cf8862010-09-26 14:18:51 -07003066
Kenny Root38cf8862010-09-26 14:18:51 -07003067 synchronized (mObbMounts) {
Jeff Sharkey27de30d2015-04-18 16:20:27 -07003068 pw.println();
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003069 pw.println("mObbMounts:");
3070 pw.increaseIndent();
3071 final Iterator<Entry<IBinder, List<ObbState>>> binders = mObbMounts.entrySet()
3072 .iterator();
Kenny Rootaf9d6672010-10-08 09:21:39 -07003073 while (binders.hasNext()) {
3074 Entry<IBinder, List<ObbState>> e = binders.next();
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003075 pw.println(e.getKey() + ":");
3076 pw.increaseIndent();
Kenny Rootaf9d6672010-10-08 09:21:39 -07003077 final List<ObbState> obbStates = e.getValue();
Kenny Root38cf8862010-09-26 14:18:51 -07003078 for (final ObbState obbState : obbStates) {
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003079 pw.println(obbState);
Kenny Root38cf8862010-09-26 14:18:51 -07003080 }
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003081 pw.decreaseIndent();
Kenny Root38cf8862010-09-26 14:18:51 -07003082 }
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003083 pw.decreaseIndent();
Kenny Rootaf9d6672010-10-08 09:21:39 -07003084
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003085 pw.println();
3086 pw.println("mObbPathToStateMap:");
3087 pw.increaseIndent();
Kenny Rootaf9d6672010-10-08 09:21:39 -07003088 final Iterator<Entry<String, ObbState>> maps = mObbPathToStateMap.entrySet().iterator();
3089 while (maps.hasNext()) {
3090 final Entry<String, ObbState> e = maps.next();
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003091 pw.print(e.getKey());
3092 pw.print(" -> ");
3093 pw.println(e.getValue());
Kenny Rootaf9d6672010-10-08 09:21:39 -07003094 }
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003095 pw.decreaseIndent();
Kenny Root38cf8862010-09-26 14:18:51 -07003096 }
Kenny Root4161f9b2011-07-13 09:48:33 -07003097
Robert Greenwalt470fd722012-01-18 12:51:15 -08003098 pw.println();
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003099 pw.println("mConnection:");
3100 pw.increaseIndent();
Robert Greenwalt470fd722012-01-18 12:51:15 -08003101 mConnector.dump(fd, pw, args);
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003102 pw.decreaseIndent();
Christopher Tate7265abe2014-11-21 13:54:45 -08003103
3104 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
3105
3106 pw.println();
3107 pw.print("Last maintenance: ");
3108 pw.println(sdf.format(new Date(mLastMaintenance)));
Kenny Root38cf8862010-09-26 14:18:51 -07003109 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003110
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -07003111 /** {@inheritDoc} */
Jeff Sharkey48877892015-03-18 11:27:19 -07003112 @Override
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -07003113 public void monitor() {
3114 if (mConnector != null) {
3115 mConnector.monitor();
3116 }
3117 }
3118}