blob: c3d32c2a870b401bec909472de9a55b5cb3136ef [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;
Jeff Sharkeye8a4b662015-06-27 15:43:45 -070021import static com.android.internal.util.XmlUtils.readLongAttribute;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070022import static com.android.internal.util.XmlUtils.readStringAttribute;
Jeff Sharkey4c099d02015-05-15 13:45:00 -070023import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070024import static com.android.internal.util.XmlUtils.writeIntAttribute;
Jeff Sharkeye8a4b662015-06-27 15:43:45 -070025import static com.android.internal.util.XmlUtils.writeLongAttribute;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070026import static com.android.internal.util.XmlUtils.writeStringAttribute;
27import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
28import static org.xmlpull.v1.XmlPullParser.START_TAG;
29
Jason parks8888c592011-01-20 22:46:41 -060030import android.Manifest;
Jeff Sharkeyef10ee02015-07-05 14:17:27 -070031import android.annotation.Nullable;
Elliott Hughesf839b4f2014-09-26 12:30:47 -070032import android.app.ActivityManagerNative;
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -070033import android.app.AppOpsManager;
Jeff Sharkey14cbe522015-07-08 14:06:37 -070034import android.app.IActivityManager;
Jeff Sharkeybcd262d2015-06-10 09:41:17 -070035import android.content.BroadcastReceiver;
Kenny Roota02b8b02010-08-05 16:14:17 -070036import android.content.ComponentName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037import android.content.Context;
38import android.content.Intent;
Jeff Sharkeybcd262d2015-06-10 09:41:17 -070039import android.content.IntentFilter;
Kenny Roota02b8b02010-08-05 16:14:17 -070040import android.content.ServiceConnection;
Jeff Sharkey275e3e42015-04-24 16:10:32 -070041import android.content.pm.IPackageMoveObserver;
42import android.content.pm.PackageManager;
Jeff Sharkey14cbe522015-07-08 14:06:37 -070043import android.content.pm.ProviderInfo;
Jeff Sharkeybcd262d2015-06-10 09:41:17 -070044import android.content.pm.UserInfo;
Elliott Hughesf839b4f2014-09-26 12:30:47 -070045import android.content.res.Configuration;
Kenny Root02c87302010-07-01 08:10:18 -070046import android.content.res.ObbInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047import android.net.Uri;
Kenny Root02c87302010-07-01 08:10:18 -070048import android.os.Binder;
Jeff Sharkey4c099d02015-05-15 13:45:00 -070049import android.os.DropBoxManager;
Kenny Roota02b8b02010-08-05 16:14:17 -070050import android.os.Environment;
Jeff Sharkeyb049e212012-09-07 23:16:01 -070051import android.os.Environment.UserEnvironment;
Jeff Sharkey48877892015-03-18 11:27:19 -070052import android.os.FileUtils;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080053import android.os.Handler;
Dianne Hackbornefa92b22013-05-03 14:11:43 -070054import android.os.HandlerThread;
Kenny Roota02b8b02010-08-05 16:14:17 -070055import android.os.IBinder;
Daniel Sandler5f27ef42010-03-16 15:42:02 -040056import android.os.Looper;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080057import android.os.Message;
Jeff Sharkey9527b222015-06-24 15:24:48 -070058import android.os.Process;
Jeff Sharkey7151a9a2015-04-04 15:22:37 -070059import android.os.RemoteCallbackList;
San Mehat4270e1e2010-01-29 05:32:19 -080060import android.os.RemoteException;
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -080061import android.os.ServiceManager;
Jeff Sharkeyedcdaf62015-07-09 09:45:36 -070062import android.os.SystemClock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063import android.os.SystemProperties;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070064import android.os.UserHandle;
Emily Bernier92aa5a22014-07-07 10:11:48 -040065import android.os.UserManager;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070066import android.os.storage.DiskInfo;
Kenny Roota02b8b02010-08-05 16:14:17 -070067import android.os.storage.IMountService;
68import android.os.storage.IMountServiceListener;
69import android.os.storage.IMountShutdownObserver;
70import android.os.storage.IObbActionListener;
Svet Ganov6ee871e2015-07-10 14:29:33 -070071import android.os.storage.MountServiceInternal;
Kenny Rootaf9d6672010-10-08 09:21:39 -070072import android.os.storage.OnObbStateChangeListener;
Paul Lawrence46791e72014-04-03 09:10:26 -070073import android.os.storage.StorageManager;
Kenny Roota02b8b02010-08-05 16:14:17 -070074import android.os.storage.StorageResultCode;
Mike Lockwood2f6a3882011-05-09 19:08:06 -070075import android.os.storage.StorageVolume;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070076import android.os.storage.VolumeInfo;
Jeff Sharkeyb36586a2015-04-27 08:42:28 -070077import android.os.storage.VolumeRecord;
Jeff Sharkey14cbe522015-07-08 14:06:37 -070078import android.provider.MediaStore;
Jeff Sharkeye8a4b662015-06-27 15:43:45 -070079import android.provider.Settings;
Jason parksf7b3cd42011-01-27 09:28:25 -060080import android.text.TextUtils;
Jeff Sharkey1783f142015-04-17 10:52:51 -070081import android.text.format.DateUtils;
Jeff Sharkey48877892015-03-18 11:27:19 -070082import android.util.ArrayMap;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070083import android.util.AtomicFile;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070084import android.util.Log;
San Mehata5078592010-03-25 09:36:54 -070085import android.util.Slog;
Jeff Sharkeye8a4b662015-06-27 15:43:45 -070086import android.util.TimeUtils;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070087import android.util.Xml;
Jeff Sharkey48877892015-03-18 11:27:19 -070088
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070089import libcore.io.IoUtils;
Jeff Sharkey48877892015-03-18 11:27:19 -070090import libcore.util.EmptyArray;
Mike Lockwood2f6a3882011-05-09 19:08:06 -070091
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -080092import com.android.internal.annotations.GuardedBy;
93import com.android.internal.annotations.VisibleForTesting;
Jeff Sharkeyb049e212012-09-07 23:16:01 -070094import com.android.internal.app.IMediaContainerService;
Jeff Sharkey7151a9a2015-04-04 15:22:37 -070095import com.android.internal.os.SomeArgs;
Jeff Sharkey9527b222015-06-24 15:24:48 -070096import com.android.internal.os.Zygote;
Jeff Sharkey48877892015-03-18 11:27:19 -070097import com.android.internal.util.ArrayUtils;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070098import com.android.internal.util.FastXmlSerializer;
Jeff Sharkey5aca2b82013-10-16 16:21:54 -070099import com.android.internal.util.IndentingPrintWriter;
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700100import com.android.internal.util.Preconditions;
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700101import com.android.server.NativeDaemonConnector.Command;
Jeff Sharkey56cd6462013-06-07 15:09:15 -0700102import com.android.server.NativeDaemonConnector.SensitiveArg;
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700103import com.android.server.pm.PackageManagerService;
Kenny Roota02b8b02010-08-05 16:14:17 -0700104
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700105import org.xmlpull.v1.XmlPullParser;
106import org.xmlpull.v1.XmlPullParserException;
107import org.xmlpull.v1.XmlSerializer;
108
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700109import java.io.File;
Kenny Root38cf8862010-09-26 14:18:51 -0700110import java.io.FileDescriptor;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700111import java.io.FileInputStream;
112import java.io.FileNotFoundException;
Christopher Tate7265abe2014-11-21 13:54:45 -0800113import java.io.FileOutputStream;
Kenny Root05105f72010-09-22 17:29:43 -0700114import java.io.IOException;
Kenny Root38cf8862010-09-26 14:18:51 -0700115import java.io.PrintWriter;
Kenny Root3b1abba2010-10-13 15:00:07 -0700116import java.math.BigInteger;
Paul Lawrence8e397362014-01-27 15:22:30 -0800117import java.nio.charset.StandardCharsets;
Kenny Root735de3b2010-09-30 14:11:39 -0700118import java.security.NoSuchAlgorithmException;
Kenny Root3b1abba2010-10-13 15:00:07 -0700119import java.security.spec.InvalidKeySpecException;
120import java.security.spec.KeySpec;
San Mehat22dd86e2010-01-12 12:21:18 -0800121import java.util.ArrayList;
Kenny Roota02b8b02010-08-05 16:14:17 -0700122import java.util.HashMap;
San Mehat6cdd9c02010-02-09 14:45:20 -0800123import java.util.HashSet;
Kenny Root38cf8862010-09-26 14:18:51 -0700124import java.util.Iterator;
Kenny Roota02b8b02010-08-05 16:14:17 -0700125import java.util.LinkedList;
126import java.util.List;
Elliott Hughesf839b4f2014-09-26 12:30:47 -0700127import java.util.Locale;
Kenny Roota02b8b02010-08-05 16:14:17 -0700128import java.util.Map;
Kenny Root38cf8862010-09-26 14:18:51 -0700129import java.util.Map.Entry;
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700130import java.util.Objects;
Svet Ganov6ee871e2015-07-10 14:29:33 -0700131import java.util.concurrent.CopyOnWriteArrayList;
Kenny Root51a573c2012-05-17 13:30:28 -0700132import java.util.concurrent.CountDownLatch;
133import java.util.concurrent.TimeUnit;
Jeff Sharkeyedcdaf62015-07-09 09:45:36 -0700134import java.util.concurrent.TimeoutException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800135
Kenny Root3b1abba2010-10-13 15:00:07 -0700136import javax.crypto.SecretKey;
137import javax.crypto.SecretKeyFactory;
138import javax.crypto.spec.PBEKeySpec;
139
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800140/**
Jeff Sharkey48877892015-03-18 11:27:19 -0700141 * Service responsible for various storage media. Connects to {@code vold} to
142 * watch for and manage dynamically added storage, such as SD cards and USB mass
143 * storage. Also decides how storage should be presented to users on the device.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800144 */
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -0700145class MountService extends IMountService.Stub
146 implements INativeDaemonConnectorCallbacks, Watchdog.Monitor {
Jason parks5af0b912010-11-29 09:05:25 -0600147
Christopher Tated417d622013-08-19 16:14:25 -0700148 // Static direct instance pointer for the tightly-coupled idle service to use
149 static MountService sSelf = null;
150
Jeff Sharkey56e62932015-03-21 20:41:00 -0700151 public static class Lifecycle extends SystemService {
152 private MountService mMountService;
153
154 public Lifecycle(Context context) {
155 super(context);
156 }
157
158 @Override
159 public void onStart() {
160 mMountService = new MountService(getContext());
161 publishBinderService("mount", mMountService);
162 }
163
164 @Override
165 public void onBootPhase(int phase) {
166 if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
167 mMountService.systemReady();
Yasuhiro Matsuda87a38b52015-07-24 22:10:16 +0900168 } else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
169 mMountService.bootCompleted();
Jeff Sharkey56e62932015-03-21 20:41:00 -0700170 }
171 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700172
173 @Override
174 public void onStartUser(int userHandle) {
175 mMountService.onStartUser(userHandle);
176 }
177
178 @Override
179 public void onCleanupUser(int userHandle) {
180 mMountService.onCleanupUser(userHandle);
181 }
Jeff Sharkey56e62932015-03-21 20:41:00 -0700182 }
183
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800184 private static final boolean DEBUG_EVENTS = false;
Kenny Rootb7db2722011-01-25 16:39:35 -0800185 private static final boolean DEBUG_OBB = false;
Kenny Root02c87302010-07-01 08:10:18 -0700186
Kenny Root07714d42011-08-17 17:49:28 -0700187 // Disable this since it messes up long-running cryptfs operations.
188 private static final boolean WATCHDOG_ENABLE = false;
189
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800190 private static final String TAG = "MountService";
Jeff Sharkeye8a4b662015-06-27 15:43:45 -0700191
Jeff Sharkey9756d752015-05-14 21:07:42 -0700192 private static final String TAG_STORAGE_BENCHMARK = "storage_benchmark";
Jeff Sharkeye8a4b662015-06-27 15:43:45 -0700193 private static final String TAG_STORAGE_TRIM = "storage_trim";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194
Kenny Root305bcbf2010-09-03 07:56:38 -0700195 private static final String VOLD_TAG = "VoldConnector";
Paul Lawrence1c62cbb2015-06-03 14:14:52 -0700196 private static final String CRYPTD_TAG = "CryptdConnector";
Kenny Root305bcbf2010-09-03 07:56:38 -0700197
Kenny Rootcf0b38c2011-03-22 14:17:59 -0700198 /** Maximum number of ASEC containers allowed to be mounted. */
199 private static final int MAX_CONTAINERS = 250;
200
Jeff Sharkey275e3e42015-04-24 16:10:32 -0700201 /** Magic value sent by MoveTask.cpp */
202 private static final int MOVE_STATUS_COPY_FINISHED = 82;
203
San Mehat4270e1e2010-01-29 05:32:19 -0800204 /*
205 * Internal vold response code constants
206 */
San Mehat22dd86e2010-01-12 12:21:18 -0800207 class VoldResponseCode {
San Mehat4270e1e2010-01-29 05:32:19 -0800208 /*
209 * 100 series - Requestion action was initiated; expect another reply
210 * before proceeding with a new command.
211 */
San Mehat22dd86e2010-01-12 12:21:18 -0800212 public static final int VolumeListResult = 110;
213 public static final int AsecListResult = 111;
San Mehatc1b4ce92010-02-16 17:13:03 -0800214 public static final int StorageUsersListResult = 112;
Paul Lawrencee51dcf92014-03-18 10:56:00 -0700215 public static final int CryptfsGetfieldResult = 113;
San Mehat22dd86e2010-01-12 12:21:18 -0800216
San Mehat4270e1e2010-01-29 05:32:19 -0800217 /*
218 * 200 series - Requestion action has been successfully completed.
219 */
220 public static final int ShareStatusResult = 210;
San Mehat22dd86e2010-01-12 12:21:18 -0800221 public static final int AsecPathResult = 211;
San Mehat4270e1e2010-01-29 05:32:19 -0800222 public static final int ShareEnabledResult = 212;
San Mehat22dd86e2010-01-12 12:21:18 -0800223
San Mehat4270e1e2010-01-29 05:32:19 -0800224 /*
225 * 400 series - Command was accepted, but the requested action
226 * did not take place.
227 */
228 public static final int OpFailedNoMedia = 401;
229 public static final int OpFailedMediaBlank = 402;
230 public static final int OpFailedMediaCorrupt = 403;
231 public static final int OpFailedVolNotMounted = 404;
San Mehatd9709982010-02-18 11:43:03 -0800232 public static final int OpFailedStorageBusy = 405;
San Mehat2d66cef2010-03-23 11:12:52 -0700233 public static final int OpFailedStorageNotFound = 406;
San Mehat4270e1e2010-01-29 05:32:19 -0800234
235 /*
236 * 600 series - Unsolicited broadcasts.
237 */
Jeff Sharkey48877892015-03-18 11:27:19 -0700238 public static final int DISK_CREATED = 640;
239 public static final int DISK_SIZE_CHANGED = 641;
240 public static final int DISK_LABEL_CHANGED = 642;
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700241 public static final int DISK_SCANNED = 643;
Jeff Sharkeye8a4b662015-06-27 15:43:45 -0700242 public static final int DISK_SYS_PATH_CHANGED = 644;
Jeff Sharkey48877892015-03-18 11:27:19 -0700243 public static final int DISK_DESTROYED = 649;
244
245 public static final int VOLUME_CREATED = 650;
246 public static final int VOLUME_STATE_CHANGED = 651;
247 public static final int VOLUME_FS_TYPE_CHANGED = 652;
248 public static final int VOLUME_FS_UUID_CHANGED = 653;
249 public static final int VOLUME_FS_LABEL_CHANGED = 654;
250 public static final int VOLUME_PATH_CHANGED = 655;
Jeff Sharkey50a05452015-04-29 11:24:52 -0700251 public static final int VOLUME_INTERNAL_PATH_CHANGED = 656;
Jeff Sharkey48877892015-03-18 11:27:19 -0700252 public static final int VOLUME_DESTROYED = 659;
Svetoslavf23b64d2013-04-25 14:45:54 -0700253
Jeff Sharkey275e3e42015-04-24 16:10:32 -0700254 public static final int MOVE_STATUS = 660;
Jeff Sharkey9756d752015-05-14 21:07:42 -0700255 public static final int BENCHMARK_RESULT = 661;
Jeff Sharkeye8a4b662015-06-27 15:43:45 -0700256 public static final int TRIM_RESULT = 662;
San Mehat22dd86e2010-01-12 12:21:18 -0800257 }
258
Jeff Sharkey620b32b2015-04-23 19:36:02 -0700259 private static final int VERSION_INIT = 1;
260 private static final int VERSION_ADD_PRIMARY = 2;
Jeff Sharkeyfced5342015-05-10 14:53:34 -0700261 private static final int VERSION_FIX_PRIMARY = 3;
Jeff Sharkey620b32b2015-04-23 19:36:02 -0700262
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700263 private static final String TAG_VOLUMES = "volumes";
Jeff Sharkey620b32b2015-04-23 19:36:02 -0700264 private static final String ATTR_VERSION = "version";
265 private static final String ATTR_PRIMARY_STORAGE_UUID = "primaryStorageUuid";
Jeff Sharkey4c099d02015-05-15 13:45:00 -0700266 private static final String ATTR_FORCE_ADOPTABLE = "forceAdoptable";
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700267 private static final String TAG_VOLUME = "volume";
268 private static final String ATTR_TYPE = "type";
269 private static final String ATTR_FS_UUID = "fsUuid";
Jeff Sharkey5cc0df22015-06-17 19:44:05 -0700270 private static final String ATTR_PART_GUID = "partGuid";
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700271 private static final String ATTR_NICKNAME = "nickname";
272 private static final String ATTR_USER_FLAGS = "userFlags";
Jeff Sharkeye8a4b662015-06-27 15:43:45 -0700273 private static final String ATTR_CREATED_MILLIS = "createdMillis";
274 private static final String ATTR_LAST_TRIM_MILLIS = "lastTrimMillis";
275 private static final String ATTR_LAST_BENCH_MILLIS = "lastBenchMillis";
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700276
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700277 private final AtomicFile mSettingsFile;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700278
Jeff Sharkey48877892015-03-18 11:27:19 -0700279 /**
280 * <em>Never</em> hold the lock while performing downcalls into vold, since
281 * unsolicited events can suddenly appear to update data structures.
282 */
283 private final Object mLock = new Object();
284
285 @GuardedBy("mLock")
286 private int[] mStartedUsers = EmptyArray.INT;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700287
288 /** Map from disk ID to disk */
Jeff Sharkey48877892015-03-18 11:27:19 -0700289 @GuardedBy("mLock")
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700290 private ArrayMap<String, DiskInfo> mDisks = new ArrayMap<>();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700291 /** Map from volume ID to disk */
Jeff Sharkey48877892015-03-18 11:27:19 -0700292 @GuardedBy("mLock")
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700293 private ArrayMap<String, VolumeInfo> mVolumes = new ArrayMap<>();
Jeff Sharkey48877892015-03-18 11:27:19 -0700294
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700295 /** Map from UUID to record */
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700296 @GuardedBy("mLock")
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700297 private ArrayMap<String, VolumeRecord> mRecords = new ArrayMap<>();
Jeff Sharkey620b32b2015-04-23 19:36:02 -0700298 @GuardedBy("mLock")
299 private String mPrimaryStorageUuid;
Jeff Sharkey4c099d02015-05-15 13:45:00 -0700300 @GuardedBy("mLock")
301 private boolean mForceAdoptable;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700302
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700303 /** Map from disk ID to latches */
304 @GuardedBy("mLock")
305 private ArrayMap<String, CountDownLatch> mDiskScanLatches = new ArrayMap<>();
306
Jeff Sharkey275e3e42015-04-24 16:10:32 -0700307 @GuardedBy("mLock")
308 private IPackageMoveObserver mMoveCallback;
309 @GuardedBy("mLock")
310 private String mMoveTargetUuid;
311
Jeff Sharkeyef10ee02015-07-05 14:17:27 -0700312 private VolumeInfo findVolumeByIdOrThrow(String id) {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700313 synchronized (mLock) {
314 final VolumeInfo vol = mVolumes.get(id);
315 if (vol != null) {
316 return vol;
317 }
318 }
319 throw new IllegalArgumentException("No volume found for ID " + id);
320 }
321
Jeff Sharkeyef10ee02015-07-05 14:17:27 -0700322 private String findVolumeIdForPathOrThrow(String path) {
Jeff Sharkey48877892015-03-18 11:27:19 -0700323 synchronized (mLock) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700324 for (int i = 0; i < mVolumes.size(); i++) {
325 final VolumeInfo vol = mVolumes.valueAt(i);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700326 if (vol.path != null && path.startsWith(vol.path)) {
327 return vol.id;
Jeff Sharkey48877892015-03-18 11:27:19 -0700328 }
329 }
330 }
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700331 throw new IllegalArgumentException("No volume found for path " + path);
Jeff Sharkey48877892015-03-18 11:27:19 -0700332 }
333
Jeff Sharkeye8a4b662015-06-27 15:43:45 -0700334 private VolumeRecord findRecordForPath(String path) {
335 synchronized (mLock) {
336 for (int i = 0; i < mVolumes.size(); i++) {
337 final VolumeInfo vol = mVolumes.valueAt(i);
338 if (vol.path != null && path.startsWith(vol.path)) {
339 return mRecords.get(vol.fsUuid);
340 }
341 }
342 }
343 return null;
344 }
345
346 private String scrubPath(String path) {
347 if (path.startsWith(Environment.getDataDirectory().getAbsolutePath())) {
348 return "internal";
349 }
350 final VolumeRecord rec = findRecordForPath(path);
351 if (rec == null || rec.createdMillis == 0) {
352 return "unknown";
353 } else {
354 return "ext:" + (int) ((System.currentTimeMillis() - rec.createdMillis)
355 / DateUtils.WEEK_IN_MILLIS) + "w";
356 }
357 }
358
Jeff Sharkeyef10ee02015-07-05 14:17:27 -0700359 private @Nullable VolumeInfo findStorageForUuid(String volumeUuid) {
Jeff Sharkey275e3e42015-04-24 16:10:32 -0700360 final StorageManager storage = mContext.getSystemService(StorageManager.class);
361 if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) {
Jeff Sharkeyef10ee02015-07-05 14:17:27 -0700362 return storage.findVolumeById(VolumeInfo.ID_EMULATED_INTERNAL);
Jeff Sharkey275e3e42015-04-24 16:10:32 -0700363 } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
364 return storage.getPrimaryPhysicalVolume();
365 } else {
366 return storage.findEmulatedForPrivate(storage.findVolumeByUuid(volumeUuid));
367 }
368 }
369
Jeff Sharkeye8a4b662015-06-27 15:43:45 -0700370 private boolean shouldBenchmark() {
371 final long benchInterval = Settings.Global.getLong(mContext.getContentResolver(),
372 Settings.Global.STORAGE_BENCHMARK_INTERVAL, DateUtils.WEEK_IN_MILLIS);
Jeff Sharkeye83d8a92015-09-09 14:53:38 -0700373 if (benchInterval == -1) {
374 return false;
375 } else if (benchInterval == 0) {
376 return true;
377 }
378
Jeff Sharkeye8a4b662015-06-27 15:43:45 -0700379 synchronized (mLock) {
380 for (int i = 0; i < mVolumes.size(); i++) {
381 final VolumeInfo vol = mVolumes.valueAt(i);
382 final VolumeRecord rec = mRecords.get(vol.fsUuid);
Jeff Sharkeye83d8a92015-09-09 14:53:38 -0700383 if (vol.isMountedWritable() && rec != null) {
Jeff Sharkeye8a4b662015-06-27 15:43:45 -0700384 final long benchAge = System.currentTimeMillis() - rec.lastBenchMillis;
385 if (benchAge >= benchInterval) {
386 return true;
387 }
388 }
389 }
390 return false;
391 }
392 }
393
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700394 private CountDownLatch findOrCreateDiskScanLatch(String diskId) {
395 synchronized (mLock) {
396 CountDownLatch latch = mDiskScanLatches.get(diskId);
397 if (latch == null) {
398 latch = new CountDownLatch(1);
399 mDiskScanLatches.put(diskId, latch);
400 }
401 return latch;
402 }
403 }
404
Paul Lawrence8e397362014-01-27 15:22:30 -0800405 /** List of crypto types.
406 * These must match CRYPT_TYPE_XXX in cryptfs.h AND their
407 * corresponding commands in CommandListener.cpp */
408 public static final String[] CRYPTO_TYPES
409 = { "password", "default", "pattern", "pin" };
410
Brian Carlstrom7395a8a2014-04-28 22:11:01 -0700411 private final Context mContext;
Brian Carlstromdfad99a2014-05-07 15:21:14 -0700412 private final NativeDaemonConnector mConnector;
Paul Lawrence1c62cbb2015-06-03 14:14:52 -0700413 private final NativeDaemonConnector mCryptConnector;
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700414
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700415 private volatile boolean mSystemReady = false;
Yasuhiro Matsuda87a38b52015-07-24 22:10:16 +0900416 private volatile boolean mBootCompleted = false;
Jeff Sharkey48877892015-03-18 11:27:19 -0700417 private volatile boolean mDaemonConnected = false;
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700418
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700419 private PackageManagerService mPms;
420
421 private final Callbacks mCallbacks;
Jeff Sharkey48877892015-03-18 11:27:19 -0700422
Paul Lawrence1c62cbb2015-06-03 14:14:52 -0700423 // Two connectors - mConnector & mCryptConnector
424 private final CountDownLatch mConnectedSignal = new CountDownLatch(2);
Jeff Sharkey0be607c2012-11-14 14:39:19 -0800425 private final CountDownLatch mAsecsScanned = new CountDownLatch(1);
Jeff Sharkey48877892015-03-18 11:27:19 -0700426
427 private final Object mUnmountLock = new Object();
428 @GuardedBy("mUnmountLock")
429 private CountDownLatch mUnmountSignal;
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -0800430
San Mehat6cdd9c02010-02-09 14:45:20 -0800431 /**
432 * Private hash of currently mounted secure containers.
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800433 * Used as a lock in methods to manipulate secure containers.
San Mehat6cdd9c02010-02-09 14:45:20 -0800434 */
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800435 final private HashSet<String> mAsecMountSet = new HashSet<String>();
San Mehat6cdd9c02010-02-09 14:45:20 -0800436
Kenny Root02c87302010-07-01 08:10:18 -0700437 /**
Kenny Root3b1abba2010-10-13 15:00:07 -0700438 * The size of the crypto algorithm key in bits for OBB files. Currently
439 * Twofish is used which takes 128-bit keys.
440 */
441 private static final int CRYPTO_ALGORITHM_KEY_SIZE = 128;
442
443 /**
444 * The number of times to run SHA1 in the PBKDF2 function for OBB files.
445 * 1024 is reasonably secure and not too slow.
446 */
447 private static final int PBKDF2_HASH_ROUNDS = 1024;
448
449 /**
Kenny Roota02b8b02010-08-05 16:14:17 -0700450 * Mounted OBB tracking information. Used to track the current state of all
451 * OBBs.
Kenny Root02c87302010-07-01 08:10:18 -0700452 */
Kenny Root735de3b2010-09-30 14:11:39 -0700453 final private Map<IBinder, List<ObbState>> mObbMounts = new HashMap<IBinder, List<ObbState>>();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700454
455 /** Map from raw paths to {@link ObbState}. */
Kenny Roota02b8b02010-08-05 16:14:17 -0700456 final private Map<String, ObbState> mObbPathToStateMap = new HashMap<String, ObbState>();
457
Svet Ganov6ee871e2015-07-10 14:29:33 -0700458 // Not guarded by a lock.
459 private final MountServiceInternalImpl mMountServiceInternal = new MountServiceInternalImpl();
460
Kenny Roota02b8b02010-08-05 16:14:17 -0700461 class ObbState implements IBinder.DeathRecipient {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700462 public ObbState(String rawPath, String canonicalPath, int callingUid,
463 IObbActionListener token, int nonce) {
464 this.rawPath = rawPath;
465 this.canonicalPath = canonicalPath.toString();
466
467 final int userId = UserHandle.getUserId(callingUid);
468 this.ownerPath = buildObbPath(canonicalPath, userId, false);
469 this.voldPath = buildObbPath(canonicalPath, userId, true);
470
471 this.ownerGid = UserHandle.getSharedAppGid(callingUid);
Kenny Rootaf9d6672010-10-08 09:21:39 -0700472 this.token = token;
473 this.nonce = nonce;
Kenny Roota02b8b02010-08-05 16:14:17 -0700474 }
475
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700476 final String rawPath;
477 final String canonicalPath;
478 final String ownerPath;
479 final String voldPath;
Kenny Roota02b8b02010-08-05 16:14:17 -0700480
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700481 final int ownerGid;
Kenny Roota02b8b02010-08-05 16:14:17 -0700482
Kenny Rootaf9d6672010-10-08 09:21:39 -0700483 // Token of remote Binder caller
484 final IObbActionListener token;
485
486 // Identifier to pass back to the token
487 final int nonce;
Kenny Roota02b8b02010-08-05 16:14:17 -0700488
Kenny Root735de3b2010-09-30 14:11:39 -0700489 public IBinder getBinder() {
490 return token.asBinder();
491 }
492
Kenny Roota02b8b02010-08-05 16:14:17 -0700493 @Override
494 public void binderDied() {
495 ObbAction action = new UnmountObbAction(this, true);
496 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
Kenny Root735de3b2010-09-30 14:11:39 -0700497 }
Kenny Roota02b8b02010-08-05 16:14:17 -0700498
Kenny Root5919ac62010-10-05 09:49:40 -0700499 public void link() throws RemoteException {
500 getBinder().linkToDeath(this, 0);
501 }
502
503 public void unlink() {
Kenny Root735de3b2010-09-30 14:11:39 -0700504 getBinder().unlinkToDeath(this, 0);
Kenny Roota02b8b02010-08-05 16:14:17 -0700505 }
Kenny Root38cf8862010-09-26 14:18:51 -0700506
507 @Override
508 public String toString() {
509 StringBuilder sb = new StringBuilder("ObbState{");
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700510 sb.append("rawPath=").append(rawPath);
511 sb.append(",canonicalPath=").append(canonicalPath);
512 sb.append(",ownerPath=").append(ownerPath);
513 sb.append(",voldPath=").append(voldPath);
514 sb.append(",ownerGid=").append(ownerGid);
515 sb.append(",token=").append(token);
516 sb.append(",binder=").append(getBinder());
Kenny Root38cf8862010-09-26 14:18:51 -0700517 sb.append('}');
518 return sb.toString();
519 }
Kenny Roota02b8b02010-08-05 16:14:17 -0700520 }
521
522 // OBB Action Handler
523 final private ObbActionHandler mObbActionHandler;
524
525 // OBB action handler messages
526 private static final int OBB_RUN_ACTION = 1;
527 private static final int OBB_MCS_BOUND = 2;
528 private static final int OBB_MCS_UNBIND = 3;
529 private static final int OBB_MCS_RECONNECT = 4;
Kenny Rootaf9d6672010-10-08 09:21:39 -0700530 private static final int OBB_FLUSH_MOUNT_STATE = 5;
Kenny Roota02b8b02010-08-05 16:14:17 -0700531
532 /*
533 * Default Container Service information
534 */
535 static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
536 "com.android.defcontainer", "com.android.defcontainer.DefaultContainerService");
537
538 final private DefaultContainerConnection mDefContainerConn = new DefaultContainerConnection();
539
540 class DefaultContainerConnection implements ServiceConnection {
Jeff Sharkey48877892015-03-18 11:27:19 -0700541 @Override
Kenny Roota02b8b02010-08-05 16:14:17 -0700542 public void onServiceConnected(ComponentName name, IBinder service) {
543 if (DEBUG_OBB)
544 Slog.i(TAG, "onServiceConnected");
545 IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(service);
546 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_MCS_BOUND, imcs));
547 }
548
Jeff Sharkey48877892015-03-18 11:27:19 -0700549 @Override
Kenny Roota02b8b02010-08-05 16:14:17 -0700550 public void onServiceDisconnected(ComponentName name) {
551 if (DEBUG_OBB)
552 Slog.i(TAG, "onServiceDisconnected");
553 }
554 };
555
556 // Used in the ObbActionHandler
557 private IMediaContainerService mContainerService = null;
Kenny Root02c87302010-07-01 08:10:18 -0700558
Christopher Tate7265abe2014-11-21 13:54:45 -0800559 // Last fstrim operation tracking
560 private static final String LAST_FSTRIM_FILE = "last-fstrim";
561 private final File mLastMaintenanceFile;
562 private long mLastMaintenance;
563
Kenny Root02c87302010-07-01 08:10:18 -0700564 // Handler messages
Jeff Sharkey48877892015-03-18 11:27:19 -0700565 private static final int H_SYSTEM_READY = 1;
566 private static final int H_DAEMON_CONNECTED = 2;
567 private static final int H_SHUTDOWN = 3;
568 private static final int H_FSTRIM = 4;
569 private static final int H_VOLUME_MOUNT = 5;
570 private static final int H_VOLUME_BROADCAST = 6;
Jeff Sharkeyabc3e852015-08-03 14:41:13 -0700571 private static final int H_INTERNAL_BROADCAST = 7;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800572
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400573 class MountServiceHandler extends Handler {
Jeff Sharkey48877892015-03-18 11:27:19 -0700574 public MountServiceHandler(Looper looper) {
575 super(looper);
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400576 }
577
Jason parks5af0b912010-11-29 09:05:25 -0600578 @Override
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800579 public void handleMessage(Message msg) {
580 switch (msg.what) {
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700581 case H_SYSTEM_READY: {
Jeff Sharkey48877892015-03-18 11:27:19 -0700582 handleSystemReady();
583 break;
584 }
585 case H_DAEMON_CONNECTED: {
586 handleDaemonConnected();
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700587 break;
588 }
Christopher Tated417d622013-08-19 16:14:25 -0700589 case H_FSTRIM: {
Jeff Sharkey1783f142015-04-17 10:52:51 -0700590 if (!isReady()) {
591 Slog.i(TAG, "fstrim requested, but no daemon connection yet; trying again");
Christopher Tate7618db12015-04-28 16:32:55 -0700592 sendMessageDelayed(obtainMessage(H_FSTRIM, msg.obj),
593 DateUtils.SECOND_IN_MILLIS);
594 break;
Jeff Sharkey1783f142015-04-17 10:52:51 -0700595 }
596
Christopher Tated417d622013-08-19 16:14:25 -0700597 Slog.i(TAG, "Running fstrim idle maintenance");
Christopher Tate7265abe2014-11-21 13:54:45 -0800598
599 // Remember when we kicked it off
600 try {
601 mLastMaintenance = System.currentTimeMillis();
602 mLastMaintenanceFile.setLastModified(mLastMaintenance);
603 } catch (Exception e) {
604 Slog.e(TAG, "Unable to record last fstrim!");
605 }
606
Jeff Sharkeye8a4b662015-06-27 15:43:45 -0700607 final boolean shouldBenchmark = shouldBenchmark();
Christopher Tated417d622013-08-19 16:14:25 -0700608 try {
609 // This method must be run on the main (handler) thread,
610 // so it is safe to directly call into vold.
Jeff Sharkeye8a4b662015-06-27 15:43:45 -0700611 mConnector.execute("fstrim", shouldBenchmark ? "dotrimbench" : "dotrim");
Christopher Tated417d622013-08-19 16:14:25 -0700612 } catch (NativeDaemonConnectorException ndce) {
613 Slog.e(TAG, "Failed to run fstrim!");
614 }
Christopher Tate7265abe2014-11-21 13:54:45 -0800615
Christopher Tated417d622013-08-19 16:14:25 -0700616 // invoke the completion callback, if any
Jeff Sharkeye8a4b662015-06-27 15:43:45 -0700617 // TODO: fstrim is non-blocking, so remove this useless callback
Christopher Tated417d622013-08-19 16:14:25 -0700618 Runnable callback = (Runnable) msg.obj;
619 if (callback != null) {
620 callback.run();
621 }
622 break;
623 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700624 case H_SHUTDOWN: {
625 final IMountShutdownObserver obs = (IMountShutdownObserver) msg.obj;
626 boolean success = false;
627 try {
628 success = mConnector.execute("volume", "shutdown").isClassOk();
629 } catch (NativeDaemonConnectorException ignored) {
630 }
631 if (obs != null) {
632 try {
633 obs.onShutDownComplete(success ? 0 : -1);
634 } catch (RemoteException ignored) {
635 }
636 }
637 break;
638 }
639 case H_VOLUME_MOUNT: {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700640 final VolumeInfo vol = (VolumeInfo) msg.obj;
Jeff Sharkey2e606d72015-07-27 14:19:54 -0700641 if (isMountDisallowed(vol)) {
642 Slog.i(TAG, "Ignoring mount " + vol.getId() + " due to policy");
643 break;
644 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700645 try {
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700646 mConnector.execute("volume", "mount", vol.id, vol.mountFlags,
647 vol.mountUserId);
Jeff Sharkey48877892015-03-18 11:27:19 -0700648 } catch (NativeDaemonConnectorException ignored) {
649 }
650 break;
651 }
652 case H_VOLUME_BROADCAST: {
653 final StorageVolume userVol = (StorageVolume) msg.obj;
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700654 final String envState = userVol.getState();
655 Slog.d(TAG, "Volume " + userVol.getId() + " broadcasting " + envState + " to "
Jeff Sharkey48877892015-03-18 11:27:19 -0700656 + userVol.getOwner());
657
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700658 final String action = VolumeInfo.getBroadcastForEnvironment(envState);
Jeff Sharkey48877892015-03-18 11:27:19 -0700659 if (action != null) {
660 final Intent intent = new Intent(action,
661 Uri.fromFile(userVol.getPathFile()));
662 intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, userVol);
663 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
664 mContext.sendBroadcastAsUser(intent, userVol.getOwner());
665 }
666 break;
667 }
Jeff Sharkeyabc3e852015-08-03 14:41:13 -0700668 case H_INTERNAL_BROADCAST: {
669 // Internal broadcasts aimed at system components, not for
670 // third-party apps.
671 final Intent intent = (Intent) msg.obj;
672 mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
673 android.Manifest.permission.WRITE_MEDIA_STORAGE);
674 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800675 }
676 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700677 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700678
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700679 private final Handler mHandler;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800680
Jeff Sharkeybcd262d2015-06-10 09:41:17 -0700681 private BroadcastReceiver mUserReceiver = new BroadcastReceiver() {
682 @Override
683 public void onReceive(Context context, Intent intent) {
684 final String action = intent.getAction();
685 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
686
687 try {
688 if (Intent.ACTION_USER_ADDED.equals(action)) {
689 final UserManager um = mContext.getSystemService(UserManager.class);
690 final int userSerialNumber = um.getUserSerialNumber(userId);
691 mConnector.execute("volume", "user_added", userId, userSerialNumber);
692 } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
693 mConnector.execute("volume", "user_removed", userId);
694 }
695 } catch (NativeDaemonConnectorException e) {
696 Slog.w(TAG, "Failed to send user details to vold", e);
697 }
698 }
699 };
700
Jeff Sharkey56e62932015-03-21 20:41:00 -0700701 @Override
702 public void waitForAsecScan() {
Jeff Sharkey48877892015-03-18 11:27:19 -0700703 waitForLatch(mAsecsScanned, "mAsecsScanned");
Kenny Root51a573c2012-05-17 13:30:28 -0700704 }
705
San Mehat207e5382010-02-04 20:46:54 -0800706 private void waitForReady() {
Jeff Sharkey48877892015-03-18 11:27:19 -0700707 waitForLatch(mConnectedSignal, "mConnectedSignal");
Kenny Root51a573c2012-05-17 13:30:28 -0700708 }
709
Jeff Sharkey48877892015-03-18 11:27:19 -0700710 private void waitForLatch(CountDownLatch latch, String condition) {
Jeff Sharkeyedcdaf62015-07-09 09:45:36 -0700711 try {
712 waitForLatch(latch, condition, -1);
713 } catch (TimeoutException ignored) {
714 }
715 }
716
717 private void waitForLatch(CountDownLatch latch, String condition, long timeoutMillis)
718 throws TimeoutException {
719 final long startMillis = SystemClock.elapsedRealtime();
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700720 while (true) {
Kenny Root51a573c2012-05-17 13:30:28 -0700721 try {
722 if (latch.await(5000, TimeUnit.MILLISECONDS)) {
San Mehat207e5382010-02-04 20:46:54 -0800723 return;
Kenny Root51a573c2012-05-17 13:30:28 -0700724 } else {
725 Slog.w(TAG, "Thread " + Thread.currentThread().getName()
Jeff Sharkey48877892015-03-18 11:27:19 -0700726 + " still waiting for " + condition + "...");
San Mehat207e5382010-02-04 20:46:54 -0800727 }
Kenny Root51a573c2012-05-17 13:30:28 -0700728 } catch (InterruptedException e) {
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700729 Slog.w(TAG, "Interrupt while waiting for " + condition);
San Mehat207e5382010-02-04 20:46:54 -0800730 }
Jeff Sharkeyedcdaf62015-07-09 09:45:36 -0700731 if (timeoutMillis > 0 && SystemClock.elapsedRealtime() > startMillis + timeoutMillis) {
732 throw new TimeoutException("Thread " + Thread.currentThread().getName()
733 + " gave up waiting for " + condition + " after " + timeoutMillis + "ms");
734 }
San Mehat207e5382010-02-04 20:46:54 -0800735 }
San Mehat1f6301e2010-01-07 22:40:27 -0800736 }
Kenny Root02c87302010-07-01 08:10:18 -0700737
Paul Lawrence945490c2014-03-27 16:37:28 +0000738 private boolean isReady() {
739 try {
740 return mConnectedSignal.await(0, TimeUnit.MILLISECONDS);
741 } catch (InterruptedException e) {
742 return false;
743 }
744 }
745
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700746 private void handleSystemReady() {
Jeff Sharkey9527b222015-06-24 15:24:48 -0700747 synchronized (mLock) {
748 resetIfReadyAndConnectedLocked();
749 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700750
Jeff Sharkey48877892015-03-18 11:27:19 -0700751 // Start scheduling nominally-daily fstrim operations
Christopher Tate115afda2014-06-06 19:06:26 -0700752 MountServiceIdler.scheduleIdlePass(mContext);
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700753 }
754
Jeff Sharkey14cbe522015-07-08 14:06:37 -0700755 /**
756 * MediaProvider has a ton of code that makes assumptions about storage
757 * paths never changing, so we outright kill them to pick up new state.
758 */
759 @Deprecated
760 private void killMediaProvider() {
Jeff Sharkeyb3cf9532015-07-17 15:12:39 -0700761 final long token = Binder.clearCallingIdentity();
762 try {
763 final ProviderInfo provider = mPms.resolveContentProvider(MediaStore.AUTHORITY, 0,
764 UserHandle.USER_OWNER);
765 if (provider != null) {
766 final IActivityManager am = ActivityManagerNative.getDefault();
767 try {
768 am.killApplicationWithAppId(provider.applicationInfo.packageName,
769 UserHandle.getAppId(provider.applicationInfo.uid), "vold reset");
770 } catch (RemoteException e) {
771 }
Jeff Sharkey14cbe522015-07-08 14:06:37 -0700772 }
Jeff Sharkeyb3cf9532015-07-17 15:12:39 -0700773 } finally {
774 Binder.restoreCallingIdentity(token);
Jeff Sharkey14cbe522015-07-08 14:06:37 -0700775 }
776 }
777
Amith Yamasania7892482015-08-07 11:09:05 -0700778 private void addInternalVolume() {
779 // Create a stub volume that represents internal storage
780 final VolumeInfo internal = new VolumeInfo(VolumeInfo.ID_PRIVATE_INTERNAL,
781 VolumeInfo.TYPE_PRIVATE, null, null);
782 internal.state = VolumeInfo.STATE_MOUNTED;
783 internal.path = Environment.getDataDirectory().getAbsolutePath();
784 mVolumes.put(internal.id, internal);
785 }
786
Jeff Sharkey9527b222015-06-24 15:24:48 -0700787 private void resetIfReadyAndConnectedLocked() {
Jeff Sharkey48877892015-03-18 11:27:19 -0700788 Slog.d(TAG, "Thinking about reset, mSystemReady=" + mSystemReady
789 + ", mDaemonConnected=" + mDaemonConnected);
790 if (mSystemReady && mDaemonConnected) {
Jeff Sharkey14cbe522015-07-08 14:06:37 -0700791 killMediaProvider();
792
Jeff Sharkey48877892015-03-18 11:27:19 -0700793 mDisks.clear();
794 mVolumes.clear();
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700795
Amith Yamasania7892482015-08-07 11:09:05 -0700796 addInternalVolume();
Jeff Sharkey59d577a2015-04-11 21:27:21 -0700797
Jeff Sharkey48877892015-03-18 11:27:19 -0700798 try {
799 mConnector.execute("volume", "reset");
Jeff Sharkeybcd262d2015-06-10 09:41:17 -0700800
801 // Tell vold about all existing and started users
802 final UserManager um = mContext.getSystemService(UserManager.class);
803 final List<UserInfo> users = um.getUsers();
804 for (UserInfo user : users) {
805 mConnector.execute("volume", "user_added", user.id, user.serialNumber);
806 }
Jeff Sharkey50a05452015-04-29 11:24:52 -0700807 for (int userId : mStartedUsers) {
Jeff Sharkeybcd262d2015-06-10 09:41:17 -0700808 mConnector.execute("volume", "user_started", userId);
Jeff Sharkey50a05452015-04-29 11:24:52 -0700809 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700810 } catch (NativeDaemonConnectorException e) {
811 Slog.w(TAG, "Failed to reset vold", e);
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700812 }
813 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700814 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700815
Jeff Sharkey48877892015-03-18 11:27:19 -0700816 private void onStartUser(int userId) {
817 Slog.d(TAG, "onStartUser " + userId);
818
819 // We purposefully block here to make sure that user-specific
820 // staging area is ready so it's ready for zygote-forked apps to
821 // bind mount against.
822 try {
Jeff Sharkeybcd262d2015-06-10 09:41:17 -0700823 mConnector.execute("volume", "user_started", userId);
Jeff Sharkey48877892015-03-18 11:27:19 -0700824 } catch (NativeDaemonConnectorException ignored) {
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700825 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700826
827 // Record user as started so newly mounted volumes kick off events
828 // correctly, then synthesize events for any already-mounted volumes.
829 synchronized (mVolumes) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700830 for (int i = 0; i < mVolumes.size(); i++) {
831 final VolumeInfo vol = mVolumes.valueAt(i);
Jeff Sharkey46349872015-07-28 10:49:47 -0700832 if (vol.isVisibleForRead(userId) && vol.isMountedReadable()) {
Svet Ganov6ee871e2015-07-10 14:29:33 -0700833 final StorageVolume userVol = vol.buildStorageVolume(mContext, userId, false);
Jeff Sharkey48877892015-03-18 11:27:19 -0700834 mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget();
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700835
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700836 final String envState = VolumeInfo.getEnvironmentForState(vol.getState());
837 mCallbacks.notifyStorageStateChanged(userVol.getPath(), envState, envState);
Jeff Sharkey48877892015-03-18 11:27:19 -0700838 }
839 }
840 mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userId);
841 }
842 }
843
844 private void onCleanupUser(int userId) {
845 Slog.d(TAG, "onCleanupUser " + userId);
846
847 try {
Jeff Sharkeybcd262d2015-06-10 09:41:17 -0700848 mConnector.execute("volume", "user_stopped", userId);
Jeff Sharkey48877892015-03-18 11:27:19 -0700849 } catch (NativeDaemonConnectorException ignored) {
850 }
851
852 synchronized (mVolumes) {
853 mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userId);
854 }
855 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700856
Christopher Tated417d622013-08-19 16:14:25 -0700857 void runIdleMaintenance(Runnable callback) {
858 mHandler.sendMessage(mHandler.obtainMessage(H_FSTRIM, callback));
859 }
860
Christopher Tate7265abe2014-11-21 13:54:45 -0800861 // Binder entry point for kicking off an immediate fstrim
862 @Override
863 public void runMaintenance() {
Jeff Sharkey48877892015-03-18 11:27:19 -0700864 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
Christopher Tate7265abe2014-11-21 13:54:45 -0800865 runIdleMaintenance(null);
866 }
867
868 @Override
869 public long lastMaintenance() {
870 return mLastMaintenance;
871 }
872
San Mehat4270e1e2010-01-29 05:32:19 -0800873 /**
San Mehat4270e1e2010-01-29 05:32:19 -0800874 * Callback from NativeDaemonConnector
875 */
Jeff Sharkey48877892015-03-18 11:27:19 -0700876 @Override
San Mehat4270e1e2010-01-29 05:32:19 -0800877 public void onDaemonConnected() {
Jeff Sharkey48877892015-03-18 11:27:19 -0700878 mDaemonConnected = true;
879 mHandler.obtainMessage(H_DAEMON_CONNECTED).sendToTarget();
880 }
881
882 private void handleDaemonConnected() {
Jeff Sharkey9527b222015-06-24 15:24:48 -0700883 synchronized (mLock) {
884 resetIfReadyAndConnectedLocked();
885 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700886
San Mehat4270e1e2010-01-29 05:32:19 -0800887 /*
Jeff Sharkey48877892015-03-18 11:27:19 -0700888 * Now that we've done our initialization, release
889 * the hounds!
San Mehat4270e1e2010-01-29 05:32:19 -0800890 */
Jeff Sharkey48877892015-03-18 11:27:19 -0700891 mConnectedSignal.countDown();
Paul Lawrence1c62cbb2015-06-03 14:14:52 -0700892 if (mConnectedSignal.getCount() != 0) {
893 // More daemons need to connect
894 return;
895 }
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400896
Jeff Sharkey48877892015-03-18 11:27:19 -0700897 // On an encrypted device we can't see system properties yet, so pull
898 // the system locale out of the mount service.
899 if ("".equals(SystemProperties.get("vold.encrypt_progress"))) {
900 copyLocaleFromMountService();
901 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700902
Jeff Sharkey48877892015-03-18 11:27:19 -0700903 // Let package manager load internal ASECs.
904 mPms.scanAvailableAsecs();
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400905
Jeff Sharkey48877892015-03-18 11:27:19 -0700906 // Notify people waiting for ASECs to be scanned that it's done.
907 mAsecsScanned.countDown();
San Mehat4270e1e2010-01-29 05:32:19 -0800908 }
909
Elliott Hughesf839b4f2014-09-26 12:30:47 -0700910 private void copyLocaleFromMountService() {
911 String systemLocale;
912 try {
913 systemLocale = getField(StorageManager.SYSTEM_LOCALE_KEY);
914 } catch (RemoteException e) {
915 return;
916 }
917 if (TextUtils.isEmpty(systemLocale)) {
918 return;
919 }
920
921 Slog.d(TAG, "Got locale " + systemLocale + " from mount service");
922 Locale locale = Locale.forLanguageTag(systemLocale);
923 Configuration config = new Configuration();
924 config.setLocale(locale);
925 try {
926 ActivityManagerNative.getDefault().updateConfiguration(config);
927 } catch (RemoteException e) {
928 Slog.e(TAG, "Error setting system locale from mount service", e);
929 }
Elliott Hughes9c33f282014-10-13 12:39:56 -0700930
931 // Temporary workaround for http://b/17945169.
932 Slog.d(TAG, "Setting system properties to " + systemLocale + " from mount service");
Narayan Kamathd30dbb82015-01-15 14:48:15 +0000933 SystemProperties.set("persist.sys.locale", locale.toLanguageTag());
Elliott Hughesf839b4f2014-09-26 12:30:47 -0700934 }
935
San Mehat4270e1e2010-01-29 05:32:19 -0800936 /**
San Mehat4270e1e2010-01-29 05:32:19 -0800937 * Callback from NativeDaemonConnector
938 */
Jeff Sharkey48877892015-03-18 11:27:19 -0700939 @Override
Dianne Hackborn77b987f2014-02-26 16:20:52 -0800940 public boolean onCheckHoldWakeLock(int code) {
941 return false;
942 }
943
944 /**
945 * Callback from NativeDaemonConnector
946 */
Jeff Sharkey48877892015-03-18 11:27:19 -0700947 @Override
San Mehat4270e1e2010-01-29 05:32:19 -0800948 public boolean onEvent(int code, String raw, String[] cooked) {
Jeff Sharkey48877892015-03-18 11:27:19 -0700949 synchronized (mLock) {
950 return onEventLocked(code, raw, cooked);
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800951 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700952 }
Jeff Sharkey5aca2b82013-10-16 16:21:54 -0700953
Jeff Sharkey48877892015-03-18 11:27:19 -0700954 private boolean onEventLocked(int code, String raw, String[] cooked) {
955 switch (code) {
956 case VoldResponseCode.DISK_CREATED: {
957 if (cooked.length != 3) break;
958 final String id = cooked[1];
Jeff Sharkey74acbbb2015-04-21 12:14:03 -0700959 int flags = Integer.parseInt(cooked[2]);
Jeff Sharkey4c099d02015-05-15 13:45:00 -0700960 if (SystemProperties.getBoolean(StorageManager.PROP_FORCE_ADOPTABLE, false)
961 || mForceAdoptable) {
Jeff Sharkey74acbbb2015-04-21 12:14:03 -0700962 flags |= DiskInfo.FLAG_ADOPTABLE;
963 }
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700964 mDisks.put(id, new DiskInfo(id, flags));
Jeff Sharkey48877892015-03-18 11:27:19 -0700965 break;
Jeff Sharkey5aca2b82013-10-16 16:21:54 -0700966 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700967 case VoldResponseCode.DISK_SIZE_CHANGED: {
968 if (cooked.length != 3) break;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700969 final DiskInfo disk = mDisks.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -0700970 if (disk != null) {
971 disk.size = Long.parseLong(cooked[2]);
San Mehat4270e1e2010-01-29 05:32:19 -0800972 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700973 break;
974 }
975 case VoldResponseCode.DISK_LABEL_CHANGED: {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700976 final DiskInfo disk = mDisks.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -0700977 if (disk != null) {
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700978 final StringBuilder builder = new StringBuilder();
979 for (int i = 2; i < cooked.length; i++) {
980 builder.append(cooked[i]).append(' ');
981 }
982 disk.label = builder.toString().trim();
Jeff Sharkey48877892015-03-18 11:27:19 -0700983 }
984 break;
985 }
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700986 case VoldResponseCode.DISK_SCANNED: {
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700987 if (cooked.length != 2) break;
988 final DiskInfo disk = mDisks.get(cooked[1]);
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700989 if (disk != null) {
990 onDiskScannedLocked(disk);
991 }
Jeff Sharkey59d577a2015-04-11 21:27:21 -0700992 break;
993 }
Jeff Sharkeye8a4b662015-06-27 15:43:45 -0700994 case VoldResponseCode.DISK_SYS_PATH_CHANGED: {
995 if (cooked.length != 3) break;
996 final DiskInfo disk = mDisks.get(cooked[1]);
997 if (disk != null) {
998 disk.sysPath = cooked[2];
999 }
1000 break;
1001 }
Jeff Sharkey48877892015-03-18 11:27:19 -07001002 case VoldResponseCode.DISK_DESTROYED: {
1003 if (cooked.length != 2) break;
Makoto Onuki9dc575d2015-06-12 16:10:25 -07001004 final DiskInfo disk = mDisks.remove(cooked[1]);
1005 if (disk != null) {
1006 mCallbacks.notifyDiskDestroyed(disk);
1007 }
Jeff Sharkey48877892015-03-18 11:27:19 -07001008 break;
1009 }
San Mehat4270e1e2010-01-29 05:32:19 -08001010
Jeff Sharkey48877892015-03-18 11:27:19 -07001011 case VoldResponseCode.VOLUME_CREATED: {
Jeff Sharkey48877892015-03-18 11:27:19 -07001012 final String id = cooked[1];
1013 final int type = Integer.parseInt(cooked[2]);
Jeff Sharkey5cc0df22015-06-17 19:44:05 -07001014 final String diskId = TextUtils.nullIfEmpty(cooked[3]);
1015 final String partGuid = TextUtils.nullIfEmpty(cooked[4]);
1016
Jeff Sharkey27de30d2015-04-18 16:20:27 -07001017 final DiskInfo disk = mDisks.get(diskId);
Jeff Sharkey5af1835d2015-07-07 17:26:59 -07001018 final VolumeInfo vol = new VolumeInfo(id, type, disk, partGuid);
Jeff Sharkey48877892015-03-18 11:27:19 -07001019 mVolumes.put(id, vol);
1020 onVolumeCreatedLocked(vol);
1021 break;
1022 }
1023 case VoldResponseCode.VOLUME_STATE_CHANGED: {
1024 if (cooked.length != 3) break;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001025 final VolumeInfo vol = mVolumes.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -07001026 if (vol != null) {
1027 final int oldState = vol.state;
1028 final int newState = Integer.parseInt(cooked[2]);
1029 vol.state = newState;
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001030 onVolumeStateChangedLocked(vol, oldState, newState);
Jeff Sharkey48877892015-03-18 11:27:19 -07001031 }
1032 break;
1033 }
1034 case VoldResponseCode.VOLUME_FS_TYPE_CHANGED: {
1035 if (cooked.length != 3) break;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001036 final VolumeInfo vol = mVolumes.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -07001037 if (vol != null) {
1038 vol.fsType = cooked[2];
1039 }
1040 break;
1041 }
1042 case VoldResponseCode.VOLUME_FS_UUID_CHANGED: {
1043 if (cooked.length != 3) break;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001044 final VolumeInfo vol = mVolumes.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -07001045 if (vol != null) {
1046 vol.fsUuid = cooked[2];
1047 }
1048 break;
1049 }
1050 case VoldResponseCode.VOLUME_FS_LABEL_CHANGED: {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001051 final VolumeInfo vol = mVolumes.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -07001052 if (vol != null) {
Jeff Sharkey27de30d2015-04-18 16:20:27 -07001053 final StringBuilder builder = new StringBuilder();
1054 for (int i = 2; i < cooked.length; i++) {
1055 builder.append(cooked[i]).append(' ');
1056 }
1057 vol.fsLabel = builder.toString().trim();
Jeff Sharkey48877892015-03-18 11:27:19 -07001058 }
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001059 // TODO: notify listeners that label changed
Jeff Sharkey48877892015-03-18 11:27:19 -07001060 break;
1061 }
1062 case VoldResponseCode.VOLUME_PATH_CHANGED: {
1063 if (cooked.length != 3) break;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001064 final VolumeInfo vol = mVolumes.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -07001065 if (vol != null) {
1066 vol.path = cooked[2];
1067 }
1068 break;
1069 }
Jeff Sharkey50a05452015-04-29 11:24:52 -07001070 case VoldResponseCode.VOLUME_INTERNAL_PATH_CHANGED: {
1071 if (cooked.length != 3) break;
1072 final VolumeInfo vol = mVolumes.get(cooked[1]);
1073 if (vol != null) {
1074 vol.internalPath = cooked[2];
1075 }
1076 break;
1077 }
Jeff Sharkey48877892015-03-18 11:27:19 -07001078 case VoldResponseCode.VOLUME_DESTROYED: {
1079 if (cooked.length != 2) break;
1080 mVolumes.remove(cooked[1]);
1081 break;
1082 }
San Mehat4270e1e2010-01-29 05:32:19 -08001083
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001084 case VoldResponseCode.MOVE_STATUS: {
1085 final int status = Integer.parseInt(cooked[1]);
1086 onMoveStatusLocked(status);
1087 break;
1088 }
Jeff Sharkey9756d752015-05-14 21:07:42 -07001089 case VoldResponseCode.BENCHMARK_RESULT: {
Jeff Sharkeye8a4b662015-06-27 15:43:45 -07001090 if (cooked.length != 7) break;
1091 final String path = cooked[1];
1092 final String ident = cooked[2];
1093 final long create = Long.parseLong(cooked[3]);
1094 final long drop = Long.parseLong(cooked[4]);
1095 final long run = Long.parseLong(cooked[5]);
1096 final long destroy = Long.parseLong(cooked[6]);
1097
Jeff Sharkey9756d752015-05-14 21:07:42 -07001098 final DropBoxManager dropBox = mContext.getSystemService(DropBoxManager.class);
Jeff Sharkeye8a4b662015-06-27 15:43:45 -07001099 dropBox.addText(TAG_STORAGE_BENCHMARK, scrubPath(path)
1100 + " " + ident + " " + create + " " + run + " " + destroy);
1101
1102 final VolumeRecord rec = findRecordForPath(path);
1103 if (rec != null) {
1104 rec.lastBenchMillis = System.currentTimeMillis();
1105 writeSettingsLocked();
1106 }
1107
1108 break;
1109 }
1110 case VoldResponseCode.TRIM_RESULT: {
1111 if (cooked.length != 4) break;
1112 final String path = cooked[1];
1113 final long bytes = Long.parseLong(cooked[2]);
1114 final long time = Long.parseLong(cooked[3]);
1115
1116 final DropBoxManager dropBox = mContext.getSystemService(DropBoxManager.class);
1117 dropBox.addText(TAG_STORAGE_TRIM, scrubPath(path)
1118 + " " + bytes + " " + time);
1119
1120 final VolumeRecord rec = findRecordForPath(path);
1121 if (rec != null) {
1122 rec.lastTrimMillis = System.currentTimeMillis();
1123 writeSettingsLocked();
1124 }
1125
Jeff Sharkey9756d752015-05-14 21:07:42 -07001126 break;
1127 }
1128
Jeff Sharkey48877892015-03-18 11:27:19 -07001129 default: {
1130 Slog.d(TAG, "Unhandled vold event " + code);
Mike Lockwooda5250c92011-05-23 13:44:04 -04001131 }
San Mehat4270e1e2010-01-29 05:32:19 -08001132 }
1133
Daniel Sandler5f27ef42010-03-16 15:42:02 -04001134 return true;
San Mehat4270e1e2010-01-29 05:32:19 -08001135 }
1136
Jeff Sharkeyeba260d2015-04-19 14:35:16 -07001137 private void onDiskScannedLocked(DiskInfo disk) {
Jeff Sharkey620b32b2015-04-23 19:36:02 -07001138 int volumeCount = 0;
Jeff Sharkeyeba260d2015-04-19 14:35:16 -07001139 for (int i = 0; i < mVolumes.size(); i++) {
1140 final VolumeInfo vol = mVolumes.valueAt(i);
1141 if (Objects.equals(disk.id, vol.getDiskId())) {
Jeff Sharkey620b32b2015-04-23 19:36:02 -07001142 volumeCount++;
Jeff Sharkeyeba260d2015-04-19 14:35:16 -07001143 }
1144 }
1145
Jeff Sharkeyc7acac62015-06-12 16:16:56 -07001146 final Intent intent = new Intent(DiskInfo.ACTION_DISK_SCANNED);
1147 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1148 intent.putExtra(DiskInfo.EXTRA_DISK_ID, disk.id);
1149 intent.putExtra(DiskInfo.EXTRA_VOLUME_COUNT, volumeCount);
Jeff Sharkeyabc3e852015-08-03 14:41:13 -07001150 mHandler.obtainMessage(H_INTERNAL_BROADCAST, intent).sendToTarget();
Jeff Sharkeyc7acac62015-06-12 16:16:56 -07001151
1152 final CountDownLatch latch = mDiskScanLatches.remove(disk.id);
1153 if (latch != null) {
1154 latch.countDown();
1155 }
1156
Jeff Sharkeyf5a6bd72015-05-19 14:42:38 -07001157 disk.volumeCount = volumeCount;
Jeff Sharkey620b32b2015-04-23 19:36:02 -07001158 mCallbacks.notifyDiskScanned(disk, volumeCount);
Jeff Sharkeyeba260d2015-04-19 14:35:16 -07001159 }
1160
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001161 private void onVolumeCreatedLocked(VolumeInfo vol) {
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001162 if (vol.type == VolumeInfo.TYPE_EMULATED) {
1163 final StorageManager storage = mContext.getSystemService(StorageManager.class);
1164 final VolumeInfo privateVol = storage.findPrivateForEmulated(vol);
1165
1166 if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, mPrimaryStorageUuid)
1167 && VolumeInfo.ID_PRIVATE_INTERNAL.equals(privateVol.id)) {
1168 Slog.v(TAG, "Found primary storage at " + vol);
1169 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
1170 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
1171 mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
1172
1173 } else if (Objects.equals(privateVol.fsUuid, mPrimaryStorageUuid)) {
1174 Slog.v(TAG, "Found primary storage at " + vol);
1175 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
1176 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
1177 mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
1178 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001179
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001180 } else if (vol.type == VolumeInfo.TYPE_PUBLIC) {
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001181 // TODO: only look at first public partition
1182 if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, mPrimaryStorageUuid)
1183 && vol.disk.isDefaultPrimary()) {
1184 Slog.v(TAG, "Found primary storage at " + vol);
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07001185 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
1186 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
San Mehat4270e1e2010-01-29 05:32:19 -08001187 }
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07001188
1189 // Adoptable public disks are visible to apps, since they meet
1190 // public API requirement of being in a stable location.
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001191 if (vol.disk.isAdoptable()) {
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07001192 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
1193 }
1194
1195 vol.mountUserId = UserHandle.USER_OWNER;
Jeff Sharkey48877892015-03-18 11:27:19 -07001196 mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
San Mehat4270e1e2010-01-29 05:32:19 -08001197
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -07001198 } else if (vol.type == VolumeInfo.TYPE_PRIVATE) {
1199 mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
1200
San Mehat4270e1e2010-01-29 05:32:19 -08001201 } else {
Jeff Sharkey48877892015-03-18 11:27:19 -07001202 Slog.d(TAG, "Skipping automatic mounting of " + vol);
San Mehat4270e1e2010-01-29 05:32:19 -08001203 }
1204 }
1205
Jeff Sharkeye6c04f92015-04-18 21:38:05 -07001206 private boolean isBroadcastWorthy(VolumeInfo vol) {
1207 switch (vol.getType()) {
Jeff Sharkeyc7acac62015-06-12 16:16:56 -07001208 case VolumeInfo.TYPE_PRIVATE:
Jeff Sharkeye6c04f92015-04-18 21:38:05 -07001209 case VolumeInfo.TYPE_PUBLIC:
1210 case VolumeInfo.TYPE_EMULATED:
1211 break;
1212 default:
1213 return false;
1214 }
1215
1216 switch (vol.getState()) {
1217 case VolumeInfo.STATE_MOUNTED:
1218 case VolumeInfo.STATE_MOUNTED_READ_ONLY:
1219 case VolumeInfo.STATE_EJECTING:
1220 case VolumeInfo.STATE_UNMOUNTED:
Jeff Sharkeyc7acac62015-06-12 16:16:56 -07001221 case VolumeInfo.STATE_UNMOUNTABLE:
Tony Mantlerf0d71052015-06-24 11:45:25 -07001222 case VolumeInfo.STATE_BAD_REMOVAL:
Jeff Sharkeye6c04f92015-04-18 21:38:05 -07001223 break;
1224 default:
1225 return false;
1226 }
1227
1228 return true;
1229 }
1230
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001231 private void onVolumeStateChangedLocked(VolumeInfo vol, int oldState, int newState) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001232 // Remember that we saw this volume so we're ready to accept user
1233 // metadata, or so we can annoy them when a private volume is ejected
1234 if (vol.isMountedReadable() && !TextUtils.isEmpty(vol.fsUuid)) {
Jeff Sharkey5cc0df22015-06-17 19:44:05 -07001235 VolumeRecord rec = mRecords.get(vol.fsUuid);
1236 if (rec == null) {
1237 rec = new VolumeRecord(vol.type, vol.fsUuid);
1238 rec.partGuid = vol.partGuid;
Jeff Sharkeye8a4b662015-06-27 15:43:45 -07001239 rec.createdMillis = System.currentTimeMillis();
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001240 if (vol.type == VolumeInfo.TYPE_PRIVATE) {
1241 rec.nickname = vol.disk.getDescription();
1242 }
1243 mRecords.put(rec.fsUuid, rec);
1244 writeSettingsLocked();
Jeff Sharkey5cc0df22015-06-17 19:44:05 -07001245 } else {
1246 // Handle upgrade case where we didn't store partition GUID
1247 if (TextUtils.isEmpty(rec.partGuid)) {
1248 rec.partGuid = vol.partGuid;
1249 writeSettingsLocked();
1250 }
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001251 }
1252 }
1253
Jeff Sharkeye6c04f92015-04-18 21:38:05 -07001254 mCallbacks.notifyVolumeStateChanged(vol, oldState, newState);
1255
Yasuhiro Matsuda87a38b52015-07-24 22:10:16 +09001256 // Do not broadcast before boot has completed to avoid launching the
1257 // processes that receive the intent unnecessarily.
1258 if (mBootCompleted && isBroadcastWorthy(vol)) {
Jeff Sharkeye6c04f92015-04-18 21:38:05 -07001259 final Intent intent = new Intent(VolumeInfo.ACTION_VOLUME_STATE_CHANGED);
Jeff Sharkeyc7acac62015-06-12 16:16:56 -07001260 intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.id);
1261 intent.putExtra(VolumeInfo.EXTRA_VOLUME_STATE, newState);
Tony Mantlerf0d71052015-06-24 11:45:25 -07001262 intent.putExtra(VolumeRecord.EXTRA_FS_UUID, vol.fsUuid);
Jeff Sharkeye6c04f92015-04-18 21:38:05 -07001263 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Jeff Sharkeyabc3e852015-08-03 14:41:13 -07001264 mHandler.obtainMessage(H_INTERNAL_BROADCAST, intent).sendToTarget();
Jeff Sharkeye6c04f92015-04-18 21:38:05 -07001265 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001266
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001267 final String oldStateEnv = VolumeInfo.getEnvironmentForState(oldState);
1268 final String newStateEnv = VolumeInfo.getEnvironmentForState(newState);
Emily Bernier92aa5a22014-07-07 10:11:48 -04001269
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001270 if (!Objects.equals(oldStateEnv, newStateEnv)) {
1271 // Kick state changed event towards all started users. Any users
1272 // started after this point will trigger additional
1273 // user-specific broadcasts.
1274 for (int userId : mStartedUsers) {
Jeff Sharkey46349872015-07-28 10:49:47 -07001275 if (vol.isVisibleForRead(userId)) {
Svet Ganov6ee871e2015-07-10 14:29:33 -07001276 final StorageVolume userVol = vol.buildStorageVolume(mContext, userId, false);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001277 mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget();
Jeff Sharkey48877892015-03-18 11:27:19 -07001278
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001279 mCallbacks.notifyStorageStateChanged(userVol.getPath(), oldStateEnv,
1280 newStateEnv);
San Mehat4270e1e2010-01-29 05:32:19 -08001281 }
1282 }
1283 }
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001284
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07001285 if (vol.type == VolumeInfo.TYPE_PUBLIC && vol.state == VolumeInfo.STATE_EJECTING) {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001286 // TODO: this should eventually be handled by new ObbVolume state changes
1287 /*
1288 * Some OBBs might have been unmounted when this volume was
1289 * unmounted, so send a message to the handler to let it know to
1290 * remove those from the list of mounted OBBS.
1291 */
1292 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(
1293 OBB_FLUSH_MOUNT_STATE, vol.path));
1294 }
San Mehat4270e1e2010-01-29 05:32:19 -08001295 }
1296
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001297 private void onMoveStatusLocked(int status) {
1298 if (mMoveCallback == null) {
1299 Slog.w(TAG, "Odd, status but no move requested");
1300 return;
1301 }
1302
1303 // TODO: estimate remaining time
1304 try {
Jeff Sharkey50a05452015-04-29 11:24:52 -07001305 mMoveCallback.onStatusChanged(-1, status, -1);
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001306 } catch (RemoteException ignored) {
1307 }
1308
1309 // We've finished copying and we're about to clean up old data, so
1310 // remember that move was successful if we get rebooted
1311 if (status == MOVE_STATUS_COPY_FINISHED) {
1312 Slog.d(TAG, "Move to " + mMoveTargetUuid + " copy phase finshed; persisting");
1313
1314 mPrimaryStorageUuid = mMoveTargetUuid;
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001315 writeSettingsLocked();
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001316 }
1317
1318 if (PackageManager.isMoveStatusFinished(status)) {
1319 Slog.d(TAG, "Move to " + mMoveTargetUuid + " finished with status " + status);
1320
1321 mMoveCallback = null;
1322 mMoveTargetUuid = null;
1323 }
1324 }
1325
Jeff Sharkey48877892015-03-18 11:27:19 -07001326 private void enforcePermission(String perm) {
1327 mContext.enforceCallingOrSelfPermission(perm, perm);
Mike Lockwooda5250c92011-05-23 13:44:04 -04001328 }
1329
Jeff Sharkey2e606d72015-07-27 14:19:54 -07001330 /**
1331 * Decide if volume is mountable per device policies.
1332 */
1333 private boolean isMountDisallowed(VolumeInfo vol) {
1334 if (vol.type == VolumeInfo.TYPE_PUBLIC || vol.type == VolumeInfo.TYPE_PRIVATE) {
1335 final UserManager userManager = mContext.getSystemService(UserManager.class);
1336 return userManager.hasUserRestriction(UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA,
1337 Binder.getCallingUserHandle());
1338 } else {
1339 return false;
Emily Bernier92aa5a22014-07-07 10:11:48 -04001340 }
1341 }
1342
Amith Yamasani462ac3a2015-06-30 14:21:01 -07001343 private void enforceAdminUser() {
1344 UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
1345 final int callingUserId = UserHandle.getCallingUserId();
1346 boolean isAdmin;
1347 long token = Binder.clearCallingIdentity();
1348 try {
1349 isAdmin = um.getUserInfo(callingUserId).isAdmin();
1350 } finally {
1351 Binder.restoreCallingIdentity(token);
1352 }
1353 if (!isAdmin) {
1354 throw new SecurityException("Only admin users can adopt sd cards");
1355 }
1356 }
1357
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001358 /**
San Mehat207e5382010-02-04 20:46:54 -08001359 * Constructs a new MountService instance
1360 *
1361 * @param context Binder context for this service
1362 */
1363 public MountService(Context context) {
Christopher Tated417d622013-08-19 16:14:25 -07001364 sSelf = this;
1365
San Mehat207e5382010-02-04 20:46:54 -08001366 mContext = context;
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001367 mCallbacks = new Callbacks(FgThread.get().getLooper());
San Mehat207e5382010-02-04 20:46:54 -08001368
San Mehat207e5382010-02-04 20:46:54 -08001369 // XXX: This will go away soon in favor of IMountServiceObserver
1370 mPms = (PackageManagerService) ServiceManager.getService("package");
1371
Dianne Hackbornefa92b22013-05-03 14:11:43 -07001372 HandlerThread hthread = new HandlerThread(TAG);
1373 hthread.start();
1374 mHandler = new MountServiceHandler(hthread.getLooper());
Daniel Sandler5f27ef42010-03-16 15:42:02 -04001375
Kenny Roota02b8b02010-08-05 16:14:17 -07001376 // Add OBB Action Handler to MountService thread.
Dianne Hackborn8d044e82013-04-30 17:24:15 -07001377 mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper());
Kenny Roota02b8b02010-08-05 16:14:17 -07001378
Christopher Tate7265abe2014-11-21 13:54:45 -08001379 // Initialize the last-fstrim tracking if necessary
1380 File dataDir = Environment.getDataDirectory();
1381 File systemDir = new File(dataDir, "system");
1382 mLastMaintenanceFile = new File(systemDir, LAST_FSTRIM_FILE);
1383 if (!mLastMaintenanceFile.exists()) {
1384 // Not setting mLastMaintenance here means that we will force an
1385 // fstrim during reboot following the OTA that installs this code.
1386 try {
1387 (new FileOutputStream(mLastMaintenanceFile)).close();
1388 } catch (IOException e) {
1389 Slog.e(TAG, "Unable to create fstrim record " + mLastMaintenanceFile.getPath());
1390 }
1391 } else {
1392 mLastMaintenance = mLastMaintenanceFile.lastModified();
1393 }
1394
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001395 mSettingsFile = new AtomicFile(
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001396 new File(Environment.getSystemSecureDirectory(), "storage.xml"));
1397
1398 synchronized (mLock) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001399 readSettingsLocked();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001400 }
1401
Svet Ganov6ee871e2015-07-10 14:29:33 -07001402 LocalServices.addService(MountServiceInternal.class, mMountServiceInternal);
1403
Marco Nelissenc34ebce2010-02-18 13:39:41 -08001404 /*
Kenny Root305bcbf2010-09-03 07:56:38 -07001405 * Create the connection to vold with a maximum queue of twice the
1406 * amount of containers we'd ever expect to have. This keeps an
1407 * "asec list" from blocking a thread repeatedly.
1408 */
Paul Lawrence1c62cbb2015-06-03 14:14:52 -07001409
Dianne Hackborn77b987f2014-02-26 16:20:52 -08001410 mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25,
1411 null);
Jeff Sharkey48877892015-03-18 11:27:19 -07001412 mConnector.setDebug(true);
Kenny Root51a573c2012-05-17 13:30:28 -07001413
Kenny Root305bcbf2010-09-03 07:56:38 -07001414 Thread thread = new Thread(mConnector, VOLD_TAG);
San Mehat207e5382010-02-04 20:46:54 -08001415 thread.start();
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -07001416
Paul Lawrence1c62cbb2015-06-03 14:14:52 -07001417 // Reuse parameters from first connector since they are tested and safe
1418 mCryptConnector = new NativeDaemonConnector(this, "cryptd",
1419 MAX_CONTAINERS * 2, CRYPTD_TAG, 25, null);
1420 mCryptConnector.setDebug(true);
1421
1422 Thread crypt_thread = new Thread(mCryptConnector, CRYPTD_TAG);
1423 crypt_thread.start();
1424
Jeff Sharkeybcd262d2015-06-10 09:41:17 -07001425 final IntentFilter userFilter = new IntentFilter();
1426 userFilter.addAction(Intent.ACTION_USER_ADDED);
1427 userFilter.addAction(Intent.ACTION_USER_REMOVED);
1428 mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);
1429
Amith Yamasania7892482015-08-07 11:09:05 -07001430 addInternalVolume();
1431
Kenny Root07714d42011-08-17 17:49:28 -07001432 // Add ourself to the Watchdog monitors if enabled.
1433 if (WATCHDOG_ENABLE) {
1434 Watchdog.getInstance().addMonitor(this);
1435 }
San Mehat207e5382010-02-04 20:46:54 -08001436 }
1437
Jeff Sharkey56e62932015-03-21 20:41:00 -07001438 private void systemReady() {
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001439 mSystemReady = true;
1440 mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget();
1441 }
1442
Yasuhiro Matsuda87a38b52015-07-24 22:10:16 +09001443 private void bootCompleted() {
1444 mBootCompleted = true;
1445 }
1446
Jeff Sharkeyfced5342015-05-10 14:53:34 -07001447 private String getDefaultPrimaryStorageUuid() {
1448 if (SystemProperties.getBoolean(StorageManager.PROP_PRIMARY_PHYSICAL, false)) {
1449 return StorageManager.UUID_PRIMARY_PHYSICAL;
1450 } else {
1451 return StorageManager.UUID_PRIVATE_INTERNAL;
1452 }
1453 }
1454
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001455 private void readSettingsLocked() {
1456 mRecords.clear();
Jeff Sharkeyfced5342015-05-10 14:53:34 -07001457 mPrimaryStorageUuid = getDefaultPrimaryStorageUuid();
Jeff Sharkey4c099d02015-05-15 13:45:00 -07001458 mForceAdoptable = false;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001459
1460 FileInputStream fis = null;
1461 try {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001462 fis = mSettingsFile.openRead();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001463 final XmlPullParser in = Xml.newPullParser();
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +01001464 in.setInput(fis, StandardCharsets.UTF_8.name());
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001465
1466 int type;
1467 while ((type = in.next()) != END_DOCUMENT) {
1468 if (type == START_TAG) {
1469 final String tag = in.getName();
Jeff Sharkey620b32b2015-04-23 19:36:02 -07001470 if (TAG_VOLUMES.equals(tag)) {
1471 final int version = readIntAttribute(in, ATTR_VERSION, VERSION_INIT);
Jeff Sharkeyfced5342015-05-10 14:53:34 -07001472 final boolean primaryPhysical = SystemProperties.getBoolean(
1473 StorageManager.PROP_PRIMARY_PHYSICAL, false);
1474 final boolean validAttr = (version >= VERSION_FIX_PRIMARY)
1475 || (version >= VERSION_ADD_PRIMARY && !primaryPhysical);
1476 if (validAttr) {
Jeff Sharkey620b32b2015-04-23 19:36:02 -07001477 mPrimaryStorageUuid = readStringAttribute(in,
1478 ATTR_PRIMARY_STORAGE_UUID);
Jeff Sharkey620b32b2015-04-23 19:36:02 -07001479 }
Jeff Sharkey4c099d02015-05-15 13:45:00 -07001480 mForceAdoptable = readBooleanAttribute(in, ATTR_FORCE_ADOPTABLE, false);
Jeff Sharkey620b32b2015-04-23 19:36:02 -07001481
1482 } else if (TAG_VOLUME.equals(tag)) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001483 final VolumeRecord rec = readVolumeRecord(in);
1484 mRecords.put(rec.fsUuid, rec);
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001485 }
1486 }
1487 }
1488 } catch (FileNotFoundException e) {
1489 // Missing metadata is okay, probably first boot
1490 } catch (IOException e) {
1491 Slog.wtf(TAG, "Failed reading metadata", e);
1492 } catch (XmlPullParserException e) {
1493 Slog.wtf(TAG, "Failed reading metadata", e);
1494 } finally {
1495 IoUtils.closeQuietly(fis);
1496 }
1497 }
1498
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001499 private void writeSettingsLocked() {
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001500 FileOutputStream fos = null;
1501 try {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001502 fos = mSettingsFile.startWrite();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001503
1504 XmlSerializer out = new FastXmlSerializer();
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +01001505 out.setOutput(fos, StandardCharsets.UTF_8.name());
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001506 out.startDocument(null, true);
1507 out.startTag(null, TAG_VOLUMES);
Jeff Sharkeyfced5342015-05-10 14:53:34 -07001508 writeIntAttribute(out, ATTR_VERSION, VERSION_FIX_PRIMARY);
Jeff Sharkey620b32b2015-04-23 19:36:02 -07001509 writeStringAttribute(out, ATTR_PRIMARY_STORAGE_UUID, mPrimaryStorageUuid);
Jeff Sharkey4c099d02015-05-15 13:45:00 -07001510 writeBooleanAttribute(out, ATTR_FORCE_ADOPTABLE, mForceAdoptable);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001511 final int size = mRecords.size();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001512 for (int i = 0; i < size; i++) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001513 final VolumeRecord rec = mRecords.valueAt(i);
1514 writeVolumeRecord(out, rec);
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001515 }
1516 out.endTag(null, TAG_VOLUMES);
1517 out.endDocument();
1518
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001519 mSettingsFile.finishWrite(fos);
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001520 } catch (IOException e) {
1521 if (fos != null) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001522 mSettingsFile.failWrite(fos);
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001523 }
1524 }
1525 }
1526
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001527 public static VolumeRecord readVolumeRecord(XmlPullParser in) throws IOException {
1528 final int type = readIntAttribute(in, ATTR_TYPE);
1529 final String fsUuid = readStringAttribute(in, ATTR_FS_UUID);
1530 final VolumeRecord meta = new VolumeRecord(type, fsUuid);
Jeff Sharkey5cc0df22015-06-17 19:44:05 -07001531 meta.partGuid = readStringAttribute(in, ATTR_PART_GUID);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001532 meta.nickname = readStringAttribute(in, ATTR_NICKNAME);
1533 meta.userFlags = readIntAttribute(in, ATTR_USER_FLAGS);
Jeff Sharkeye8a4b662015-06-27 15:43:45 -07001534 meta.createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS);
1535 meta.lastTrimMillis = readLongAttribute(in, ATTR_LAST_TRIM_MILLIS);
1536 meta.lastBenchMillis = readLongAttribute(in, ATTR_LAST_BENCH_MILLIS);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001537 return meta;
1538 }
1539
1540 public static void writeVolumeRecord(XmlSerializer out, VolumeRecord rec) throws IOException {
1541 out.startTag(null, TAG_VOLUME);
1542 writeIntAttribute(out, ATTR_TYPE, rec.type);
1543 writeStringAttribute(out, ATTR_FS_UUID, rec.fsUuid);
Jeff Sharkey5cc0df22015-06-17 19:44:05 -07001544 writeStringAttribute(out, ATTR_PART_GUID, rec.partGuid);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001545 writeStringAttribute(out, ATTR_NICKNAME, rec.nickname);
1546 writeIntAttribute(out, ATTR_USER_FLAGS, rec.userFlags);
Jeff Sharkeye8a4b662015-06-27 15:43:45 -07001547 writeLongAttribute(out, ATTR_CREATED_MILLIS, rec.createdMillis);
1548 writeLongAttribute(out, ATTR_LAST_TRIM_MILLIS, rec.lastTrimMillis);
1549 writeLongAttribute(out, ATTR_LAST_BENCH_MILLIS, rec.lastBenchMillis);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001550 out.endTag(null, TAG_VOLUME);
1551 }
1552
San Mehat207e5382010-02-04 20:46:54 -08001553 /**
San Mehat4270e1e2010-01-29 05:32:19 -08001554 * Exposed API calls below here
1555 */
1556
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001557 @Override
San Mehat4270e1e2010-01-29 05:32:19 -08001558 public void registerListener(IMountServiceListener listener) {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001559 mCallbacks.register(listener);
San Mehat4270e1e2010-01-29 05:32:19 -08001560 }
1561
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001562 @Override
San Mehat4270e1e2010-01-29 05:32:19 -08001563 public void unregisterListener(IMountServiceListener listener) {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001564 mCallbacks.unregister(listener);
San Mehat4270e1e2010-01-29 05:32:19 -08001565 }
1566
Jeff Sharkey48877892015-03-18 11:27:19 -07001567 @Override
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -08001568 public void shutdown(final IMountShutdownObserver observer) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001569 enforcePermission(android.Manifest.permission.SHUTDOWN);
San Mehat4270e1e2010-01-29 05:32:19 -08001570
San Mehata5078592010-03-25 09:36:54 -07001571 Slog.i(TAG, "Shutting down");
Jeff Sharkey48877892015-03-18 11:27:19 -07001572 mHandler.obtainMessage(H_SHUTDOWN, observer).sendToTarget();
San Mehat4270e1e2010-01-29 05:32:19 -08001573 }
1574
Jeff Sharkey48877892015-03-18 11:27:19 -07001575 @Override
San Mehatb1043402010-02-05 08:26:50 -08001576 public boolean isUsbMassStorageConnected() {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001577 throw new UnsupportedOperationException();
San Mehatb1043402010-02-05 08:26:50 -08001578 }
1579
Jeff Sharkey48877892015-03-18 11:27:19 -07001580 @Override
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001581 public void setUsbMassStorageEnabled(boolean enable) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001582 throw new UnsupportedOperationException();
San Mehatb1043402010-02-05 08:26:50 -08001583 }
1584
Jeff Sharkey48877892015-03-18 11:27:19 -07001585 @Override
San Mehatb1043402010-02-05 08:26:50 -08001586 public boolean isUsbMassStorageEnabled() {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001587 throw new UnsupportedOperationException();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001588 }
Jason parks9ed98bc2011-01-17 09:58:35 -06001589
Jeff Sharkey48877892015-03-18 11:27:19 -07001590 @Override
San Mehat4270e1e2010-01-29 05:32:19 -08001591 public String getVolumeState(String mountPoint) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001592 throw new UnsupportedOperationException();
San Mehat7fd0fee2009-12-17 07:12:23 -08001593 }
1594
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001595 @Override
Kenny Roote1ff2142010-10-12 11:20:01 -07001596 public boolean isExternalStorageEmulated() {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001597 throw new UnsupportedOperationException();
Kenny Roote1ff2142010-10-12 11:20:01 -07001598 }
1599
Jeff Sharkey48877892015-03-18 11:27:19 -07001600 @Override
San Mehat4270e1e2010-01-29 05:32:19 -08001601 public int mountVolume(String path) {
Jeff Sharkeyef10ee02015-07-05 14:17:27 -07001602 mount(findVolumeIdForPathOrThrow(path));
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001603 return 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001604 }
1605
Jeff Sharkey48877892015-03-18 11:27:19 -07001606 @Override
Ben Komalo13c71972011-09-07 16:35:56 -07001607 public void unmountVolume(String path, boolean force, boolean removeEncryption) {
Jeff Sharkeyef10ee02015-07-05 14:17:27 -07001608 unmount(findVolumeIdForPathOrThrow(path));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001609 }
1610
Jeff Sharkey48877892015-03-18 11:27:19 -07001611 @Override
San Mehat4270e1e2010-01-29 05:32:19 -08001612 public int formatVolume(String path) {
Jeff Sharkeyef10ee02015-07-05 14:17:27 -07001613 format(findVolumeIdForPathOrThrow(path));
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001614 return 0;
1615 }
1616
1617 @Override
1618 public void mount(String volId) {
1619 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1620 waitForReady();
1621
Jeff Sharkeyef10ee02015-07-05 14:17:27 -07001622 final VolumeInfo vol = findVolumeByIdOrThrow(volId);
Jeff Sharkey2e606d72015-07-27 14:19:54 -07001623 if (isMountDisallowed(vol)) {
1624 throw new SecurityException("Mounting " + volId + " restricted by policy");
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001625 }
1626 try {
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07001627 mConnector.execute("volume", "mount", vol.id, vol.mountFlags, vol.mountUserId);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001628 } catch (NativeDaemonConnectorException e) {
1629 throw e.rethrowAsParcelableException();
1630 }
1631 }
1632
1633 @Override
1634 public void unmount(String volId) {
1635 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1636 waitForReady();
1637
Jeff Sharkeyef10ee02015-07-05 14:17:27 -07001638 final VolumeInfo vol = findVolumeByIdOrThrow(volId);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001639
1640 // TODO: expand PMS to know about multiple volumes
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001641 if (vol.isPrimaryPhysical()) {
1642 final long ident = Binder.clearCallingIdentity();
1643 try {
1644 synchronized (mUnmountLock) {
1645 mUnmountSignal = new CountDownLatch(1);
1646 mPms.updateExternalMediaStatus(false, true);
1647 waitForLatch(mUnmountSignal, "mUnmountSignal");
1648 mUnmountSignal = null;
1649 }
1650 } finally {
1651 Binder.restoreCallingIdentity(ident);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001652 }
1653 }
1654
1655 try {
1656 mConnector.execute("volume", "unmount", vol.id);
1657 } catch (NativeDaemonConnectorException e) {
1658 throw e.rethrowAsParcelableException();
1659 }
1660 }
1661
1662 @Override
1663 public void format(String volId) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001664 enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
San Mehat207e5382010-02-04 20:46:54 -08001665 waitForReady();
San Mehat5b77dab2010-01-26 13:28:50 -08001666
Jeff Sharkeyef10ee02015-07-05 14:17:27 -07001667 final VolumeInfo vol = findVolumeByIdOrThrow(volId);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001668 try {
Jeff Sharkey4e83cc92015-05-27 14:38:39 -07001669 mConnector.execute("volume", "format", vol.id, "auto");
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001670 } catch (NativeDaemonConnectorException e) {
1671 throw e.rethrowAsParcelableException();
Jeff Sharkey48877892015-03-18 11:27:19 -07001672 }
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001673 }
1674
1675 @Override
Jeff Sharkey9756d752015-05-14 21:07:42 -07001676 public long benchmark(String volId) {
1677 enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
1678 waitForReady();
1679
1680 try {
Jeff Sharkey14cbe522015-07-08 14:06:37 -07001681 // TODO: make benchmark async so we don't block other commands
1682 final NativeDaemonEvent res = mConnector.execute(3 * DateUtils.MINUTE_IN_MILLIS,
1683 "volume", "benchmark", volId);
Jeff Sharkey9756d752015-05-14 21:07:42 -07001684 return Long.parseLong(res.getMessage());
Todd Kennedy8101ee62015-06-23 13:35:28 -07001685 } catch (NativeDaemonTimeoutException e) {
1686 return Long.MAX_VALUE;
Jeff Sharkey9756d752015-05-14 21:07:42 -07001687 } catch (NativeDaemonConnectorException e) {
1688 throw e.rethrowAsParcelableException();
1689 }
1690 }
1691
1692 @Override
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001693 public void partitionPublic(String diskId) {
1694 enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
1695 waitForReady();
1696
Jeff Sharkeyeba260d2015-04-19 14:35:16 -07001697 final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001698 try {
1699 mConnector.execute("volume", "partition", diskId, "public");
Jeff Sharkeyedcdaf62015-07-09 09:45:36 -07001700 waitForLatch(latch, "partitionPublic", 3 * DateUtils.MINUTE_IN_MILLIS);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001701 } catch (NativeDaemonConnectorException e) {
1702 throw e.rethrowAsParcelableException();
Jeff Sharkeyedcdaf62015-07-09 09:45:36 -07001703 } catch (TimeoutException e) {
1704 throw new IllegalStateException(e);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001705 }
1706 }
1707
1708 @Override
1709 public void partitionPrivate(String diskId) {
1710 enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
Amith Yamasani462ac3a2015-06-30 14:21:01 -07001711 enforceAdminUser();
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001712 waitForReady();
1713
Jeff Sharkeyeba260d2015-04-19 14:35:16 -07001714 final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001715 try {
1716 mConnector.execute("volume", "partition", diskId, "private");
Jeff Sharkeyedcdaf62015-07-09 09:45:36 -07001717 waitForLatch(latch, "partitionPrivate", 3 * DateUtils.MINUTE_IN_MILLIS);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001718 } catch (NativeDaemonConnectorException e) {
1719 throw e.rethrowAsParcelableException();
Jeff Sharkeyedcdaf62015-07-09 09:45:36 -07001720 } catch (TimeoutException e) {
1721 throw new IllegalStateException(e);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001722 }
1723 }
1724
1725 @Override
1726 public void partitionMixed(String diskId, int ratio) {
1727 enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
Amith Yamasani462ac3a2015-06-30 14:21:01 -07001728 enforceAdminUser();
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001729 waitForReady();
1730
Jeff Sharkeyeba260d2015-04-19 14:35:16 -07001731 final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001732 try {
1733 mConnector.execute("volume", "partition", diskId, "mixed", ratio);
Jeff Sharkeyedcdaf62015-07-09 09:45:36 -07001734 waitForLatch(latch, "partitionMixed", 3 * DateUtils.MINUTE_IN_MILLIS);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001735 } catch (NativeDaemonConnectorException e) {
1736 throw e.rethrowAsParcelableException();
Jeff Sharkeyedcdaf62015-07-09 09:45:36 -07001737 } catch (TimeoutException e) {
1738 throw new IllegalStateException(e);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001739 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001740 }
1741
Jeff Sharkey48877892015-03-18 11:27:19 -07001742 @Override
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001743 public void setVolumeNickname(String fsUuid, String nickname) {
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001744 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1745 waitForReady();
1746
Jeff Sharkey50a05452015-04-29 11:24:52 -07001747 Preconditions.checkNotNull(fsUuid);
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001748 synchronized (mLock) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001749 final VolumeRecord rec = mRecords.get(fsUuid);
1750 rec.nickname = nickname;
Jeff Sharkey50a05452015-04-29 11:24:52 -07001751 mCallbacks.notifyVolumeRecordChanged(rec);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001752 writeSettingsLocked();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001753 }
1754 }
1755
1756 @Override
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001757 public void setVolumeUserFlags(String fsUuid, int flags, int mask) {
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001758 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1759 waitForReady();
1760
Jeff Sharkey50a05452015-04-29 11:24:52 -07001761 Preconditions.checkNotNull(fsUuid);
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001762 synchronized (mLock) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001763 final VolumeRecord rec = mRecords.get(fsUuid);
1764 rec.userFlags = (rec.userFlags & ~mask) | (flags & mask);
Jeff Sharkey50a05452015-04-29 11:24:52 -07001765 mCallbacks.notifyVolumeRecordChanged(rec);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001766 writeSettingsLocked();
1767 }
1768 }
1769
1770 @Override
1771 public void forgetVolume(String fsUuid) {
1772 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1773 waitForReady();
1774
Jeff Sharkey50a05452015-04-29 11:24:52 -07001775 Preconditions.checkNotNull(fsUuid);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001776 synchronized (mLock) {
Jeff Sharkey5cc0df22015-06-17 19:44:05 -07001777 final VolumeRecord rec = mRecords.remove(fsUuid);
1778 if (rec != null && !TextUtils.isEmpty(rec.partGuid)) {
1779 forgetPartition(rec.partGuid);
1780 }
1781 mCallbacks.notifyVolumeForgotten(fsUuid);
Jeff Sharkey50a05452015-04-29 11:24:52 -07001782
1783 // If this had been primary storage, revert back to internal and
1784 // reset vold so we bind into new volume into place.
1785 if (Objects.equals(mPrimaryStorageUuid, fsUuid)) {
Jeff Sharkeyfced5342015-05-10 14:53:34 -07001786 mPrimaryStorageUuid = getDefaultPrimaryStorageUuid();
Jeff Sharkey9527b222015-06-24 15:24:48 -07001787 resetIfReadyAndConnectedLocked();
Jeff Sharkey50a05452015-04-29 11:24:52 -07001788 }
1789
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001790 writeSettingsLocked();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001791 }
1792 }
1793
Jeff Sharkey7d2af542015-05-12 15:27:15 -07001794 @Override
1795 public void forgetAllVolumes() {
1796 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1797 waitForReady();
1798
Jeff Sharkey50a05452015-04-29 11:24:52 -07001799 synchronized (mLock) {
1800 for (int i = 0; i < mRecords.size(); i++) {
1801 final String fsUuid = mRecords.keyAt(i);
Jeff Sharkey5cc0df22015-06-17 19:44:05 -07001802 final VolumeRecord rec = mRecords.valueAt(i);
1803 if (!TextUtils.isEmpty(rec.partGuid)) {
1804 forgetPartition(rec.partGuid);
1805 }
Jeff Sharkey50a05452015-04-29 11:24:52 -07001806 mCallbacks.notifyVolumeForgotten(fsUuid);
1807 }
Jeff Sharkey50a05452015-04-29 11:24:52 -07001808 mRecords.clear();
Jeff Sharkey50a05452015-04-29 11:24:52 -07001809
Jeff Sharkeyfced5342015-05-10 14:53:34 -07001810 if (!Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, mPrimaryStorageUuid)) {
1811 mPrimaryStorageUuid = getDefaultPrimaryStorageUuid();
1812 }
1813
1814 writeSettingsLocked();
Jeff Sharkey9527b222015-06-24 15:24:48 -07001815 resetIfReadyAndConnectedLocked();
Jeff Sharkey50a05452015-04-29 11:24:52 -07001816 }
1817 }
1818
Jeff Sharkey5cc0df22015-06-17 19:44:05 -07001819 private void forgetPartition(String partGuid) {
1820 try {
1821 mConnector.execute("volume", "forget_partition", partGuid);
1822 } catch (NativeDaemonConnectorException e) {
1823 Slog.w(TAG, "Failed to forget key for " + partGuid + ": " + e);
1824 }
1825 }
1826
Svet Ganov6ee871e2015-07-10 14:29:33 -07001827 private void remountUidExternalStorage(int uid, int mode) {
Jeff Sharkey9527b222015-06-24 15:24:48 -07001828 waitForReady();
1829
Svet Ganov6ee871e2015-07-10 14:29:33 -07001830 String modeName = "none";
1831 switch (mode) {
1832 case Zygote.MOUNT_EXTERNAL_DEFAULT: {
1833 modeName = "default";
1834 } break;
1835
1836 case Zygote.MOUNT_EXTERNAL_READ: {
1837 modeName = "read";
1838 } break;
1839
1840 case Zygote.MOUNT_EXTERNAL_WRITE: {
1841 modeName = "write";
1842 } break;
Jeff Sharkey9527b222015-06-24 15:24:48 -07001843 }
1844
1845 try {
Svet Ganov6ee871e2015-07-10 14:29:33 -07001846 mConnector.execute("volume", "remount_uid", uid, modeName);
Jeff Sharkey9527b222015-06-24 15:24:48 -07001847 } catch (NativeDaemonConnectorException e) {
Svet Ganov6ee871e2015-07-10 14:29:33 -07001848 Slog.w(TAG, "Failed to remount UID " + uid + " as " + modeName + ": " + e);
Jeff Sharkey9527b222015-06-24 15:24:48 -07001849 }
1850 }
1851
1852 @Override
Jeff Sharkey4c099d02015-05-15 13:45:00 -07001853 public void setDebugFlags(int flags, int mask) {
1854 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1855 waitForReady();
1856
1857 synchronized (mLock) {
1858 if ((mask & StorageManager.DEBUG_FORCE_ADOPTABLE) != 0) {
1859 mForceAdoptable = (flags & StorageManager.DEBUG_FORCE_ADOPTABLE) != 0;
1860 }
1861
1862 writeSettingsLocked();
Jeff Sharkey9527b222015-06-24 15:24:48 -07001863 resetIfReadyAndConnectedLocked();
Jeff Sharkey4c099d02015-05-15 13:45:00 -07001864 }
1865 }
1866
1867 @Override
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001868 public String getPrimaryStorageUuid() {
1869 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1870 waitForReady();
1871
Jeff Sharkey620b32b2015-04-23 19:36:02 -07001872 synchronized (mLock) {
1873 return mPrimaryStorageUuid;
1874 }
1875 }
1876
1877 @Override
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001878 public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) {
1879 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1880 waitForReady();
Jeff Sharkey620b32b2015-04-23 19:36:02 -07001881
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001882 synchronized (mLock) {
Jeff Sharkeyfced5342015-05-10 14:53:34 -07001883 if (Objects.equals(mPrimaryStorageUuid, volumeUuid)) {
1884 throw new IllegalArgumentException("Primary storage already at " + volumeUuid);
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001885 }
1886
1887 if (mMoveCallback != null) {
1888 throw new IllegalStateException("Move already in progress");
1889 }
1890 mMoveCallback = callback;
1891 mMoveTargetUuid = volumeUuid;
1892
Jeff Sharkeyfced5342015-05-10 14:53:34 -07001893 // When moving to/from primary physical volume, we probably just nuked
1894 // the current storage location, so we have nothing to move.
1895 if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, mPrimaryStorageUuid)
1896 || Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
1897 Slog.d(TAG, "Skipping move to/from primary physical");
1898 onMoveStatusLocked(MOVE_STATUS_COPY_FINISHED);
1899 onMoveStatusLocked(PackageManager.MOVE_SUCCEEDED);
Jeff Sharkey9527b222015-06-24 15:24:48 -07001900 resetIfReadyAndConnectedLocked();
Jeff Sharkeyfced5342015-05-10 14:53:34 -07001901
1902 } else {
Jeff Sharkeyef10ee02015-07-05 14:17:27 -07001903 final VolumeInfo from = findStorageForUuid(mPrimaryStorageUuid);
1904 final VolumeInfo to = findStorageForUuid(volumeUuid);
1905
1906 if (from == null) {
1907 Slog.w(TAG, "Failing move due to missing from volume " + mPrimaryStorageUuid);
1908 onMoveStatusLocked(PackageManager.MOVE_FAILED_INTERNAL_ERROR);
1909 return;
1910 } else if (to == null) {
1911 Slog.w(TAG, "Failing move due to missing to volume " + volumeUuid);
1912 onMoveStatusLocked(PackageManager.MOVE_FAILED_INTERNAL_ERROR);
1913 return;
1914 }
Jeff Sharkeyfced5342015-05-10 14:53:34 -07001915
1916 try {
1917 mConnector.execute("volume", "move_storage", from.id, to.id);
1918 } catch (NativeDaemonConnectorException e) {
1919 throw e.rethrowAsParcelableException();
1920 }
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001921 }
Jeff Sharkey620b32b2015-04-23 19:36:02 -07001922 }
1923 }
1924
1925 @Override
Mike Lockwoodecedfdc2011-06-08 15:11:59 -07001926 public int[] getStorageUsers(String path) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001927 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
San Mehatc1b4ce92010-02-16 17:13:03 -08001928 waitForReady();
1929 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001930 final String[] r = NativeDaemonEvent.filterMessageList(
1931 mConnector.executeForList("storage", "users", path),
1932 VoldResponseCode.StorageUsersListResult);
1933
San Mehatc1b4ce92010-02-16 17:13:03 -08001934 // FMT: <pid> <process name>
1935 int[] data = new int[r.length];
1936 for (int i = 0; i < r.length; i++) {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001937 String[] tok = r[i].split(" ");
San Mehatc1b4ce92010-02-16 17:13:03 -08001938 try {
1939 data[i] = Integer.parseInt(tok[0]);
1940 } catch (NumberFormatException nfe) {
San Mehata5078592010-03-25 09:36:54 -07001941 Slog.e(TAG, String.format("Error parsing pid %s", tok[0]));
San Mehatc1b4ce92010-02-16 17:13:03 -08001942 return new int[0];
1943 }
1944 }
1945 return data;
1946 } catch (NativeDaemonConnectorException e) {
San Mehata5078592010-03-25 09:36:54 -07001947 Slog.e(TAG, "Failed to retrieve storage users list", e);
San Mehatc1b4ce92010-02-16 17:13:03 -08001948 return new int[0];
1949 }
1950 }
1951
San Mehatb1043402010-02-05 08:26:50 -08001952 private void warnOnNotMounted() {
Jeff Sharkey48877892015-03-18 11:27:19 -07001953 synchronized (mLock) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001954 for (int i = 0; i < mVolumes.size(); i++) {
1955 final VolumeInfo vol = mVolumes.valueAt(i);
Jeff Sharkey27de30d2015-04-18 16:20:27 -07001956 if (vol.isPrimary() && vol.isMountedWritable()) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001957 // Cool beans, we have a mounted primary volume
1958 return;
1959 }
Jeff Sharkey32ee8312012-09-30 13:21:31 -07001960 }
San Mehatb1043402010-02-05 08:26:50 -08001961 }
Jeff Sharkey48877892015-03-18 11:27:19 -07001962
1963 Slog.w(TAG, "No primary storage mounted!");
San Mehatb1043402010-02-05 08:26:50 -08001964 }
1965
San Mehat4270e1e2010-01-29 05:32:19 -08001966 public String[] getSecureContainerList() {
Jeff Sharkey48877892015-03-18 11:27:19 -07001967 enforcePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat207e5382010-02-04 20:46:54 -08001968 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001969 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001970
San Mehat4270e1e2010-01-29 05:32:19 -08001971 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001972 return NativeDaemonEvent.filterMessageList(
1973 mConnector.executeForList("asec", "list"), VoldResponseCode.AsecListResult);
San Mehat4270e1e2010-01-29 05:32:19 -08001974 } catch (NativeDaemonConnectorException e) {
1975 return new String[0];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001976 }
1977 }
San Mehat36972292010-01-06 11:06:32 -08001978
Kenny Root6dceb882012-04-12 14:23:49 -07001979 public int createSecureContainer(String id, int sizeMb, String fstype, String key,
1980 int ownerUid, boolean external) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001981 enforcePermission(android.Manifest.permission.ASEC_CREATE);
San Mehat207e5382010-02-04 20:46:54 -08001982 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001983 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001984
San Mehatb1043402010-02-05 08:26:50 -08001985 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001986 try {
Jeff Sharkey56cd6462013-06-07 15:09:15 -07001987 mConnector.execute("asec", "create", id, sizeMb, fstype, new SensitiveArg(key),
1988 ownerUid, external ? "1" : "0");
San Mehat4270e1e2010-01-29 05:32:19 -08001989 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001990 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001991 }
San Mehata181b212010-02-11 06:50:20 -08001992
1993 if (rc == StorageResultCode.OperationSucceeded) {
1994 synchronized (mAsecMountSet) {
1995 mAsecMountSet.add(id);
1996 }
1997 }
San Mehat4270e1e2010-01-29 05:32:19 -08001998 return rc;
San Mehat36972292010-01-06 11:06:32 -08001999 }
2000
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07002001 @Override
2002 public int resizeSecureContainer(String id, int sizeMb, String key) {
Jeff Sharkey48877892015-03-18 11:27:19 -07002003 enforcePermission(android.Manifest.permission.ASEC_CREATE);
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07002004 waitForReady();
2005 warnOnNotMounted();
2006
2007 int rc = StorageResultCode.OperationSucceeded;
2008 try {
2009 mConnector.execute("asec", "resize", id, sizeMb, new SensitiveArg(key));
2010 } catch (NativeDaemonConnectorException e) {
2011 rc = StorageResultCode.OperationFailedInternalError;
2012 }
2013 return rc;
2014 }
2015
San Mehat4270e1e2010-01-29 05:32:19 -08002016 public int finalizeSecureContainer(String id) {
Jeff Sharkey48877892015-03-18 11:27:19 -07002017 enforcePermission(android.Manifest.permission.ASEC_CREATE);
San Mehatb1043402010-02-05 08:26:50 -08002018 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08002019
San Mehatb1043402010-02-05 08:26:50 -08002020 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08002021 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002022 mConnector.execute("asec", "finalize", id);
San Mehata181b212010-02-11 06:50:20 -08002023 /*
2024 * Finalization does a remount, so no need
2025 * to update mAsecMountSet
2026 */
San Mehat4270e1e2010-01-29 05:32:19 -08002027 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08002028 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08002029 }
San Mehat4270e1e2010-01-29 05:32:19 -08002030 return rc;
San Mehat36972292010-01-06 11:06:32 -08002031 }
2032
Kenny Root6dceb882012-04-12 14:23:49 -07002033 public int fixPermissionsSecureContainer(String id, int gid, String filename) {
Jeff Sharkey48877892015-03-18 11:27:19 -07002034 enforcePermission(android.Manifest.permission.ASEC_CREATE);
Kenny Root6dceb882012-04-12 14:23:49 -07002035 warnOnNotMounted();
2036
2037 int rc = StorageResultCode.OperationSucceeded;
2038 try {
2039 mConnector.execute("asec", "fixperms", id, gid, filename);
2040 /*
2041 * Fix permissions does a remount, so no need to update
2042 * mAsecMountSet
2043 */
2044 } catch (NativeDaemonConnectorException e) {
2045 rc = StorageResultCode.OperationFailedInternalError;
2046 }
2047 return rc;
2048 }
2049
San Mehatd9709982010-02-18 11:43:03 -08002050 public int destroySecureContainer(String id, boolean force) {
Jeff Sharkey48877892015-03-18 11:27:19 -07002051 enforcePermission(android.Manifest.permission.ASEC_DESTROY);
San Mehat207e5382010-02-04 20:46:54 -08002052 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08002053 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08002054
Kenny Rootaa485402010-09-14 14:49:41 -07002055 /*
2056 * Force a GC to make sure AssetManagers in other threads of the
2057 * system_server are cleaned up. We have to do this since AssetManager
2058 * instances are kept as a WeakReference and it's possible we have files
2059 * open on the external storage.
2060 */
2061 Runtime.getRuntime().gc();
2062
San Mehatb1043402010-02-05 08:26:50 -08002063 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08002064 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002065 final Command cmd = new Command("asec", "destroy", id);
2066 if (force) {
2067 cmd.appendArg("force");
2068 }
2069 mConnector.execute(cmd);
San Mehat4270e1e2010-01-29 05:32:19 -08002070 } catch (NativeDaemonConnectorException e) {
San Mehatd9709982010-02-18 11:43:03 -08002071 int code = e.getCode();
2072 if (code == VoldResponseCode.OpFailedStorageBusy) {
2073 rc = StorageResultCode.OperationFailedStorageBusy;
2074 } else {
2075 rc = StorageResultCode.OperationFailedInternalError;
2076 }
San Mehat02735bc2010-01-26 15:18:08 -08002077 }
San Mehata181b212010-02-11 06:50:20 -08002078
2079 if (rc == StorageResultCode.OperationSucceeded) {
2080 synchronized (mAsecMountSet) {
2081 if (mAsecMountSet.contains(id)) {
2082 mAsecMountSet.remove(id);
2083 }
2084 }
2085 }
2086
San Mehat4270e1e2010-01-29 05:32:19 -08002087 return rc;
San Mehat36972292010-01-06 11:06:32 -08002088 }
Jason parks9ed98bc2011-01-17 09:58:35 -06002089
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07002090 public int mountSecureContainer(String id, String key, int ownerUid, boolean readOnly) {
Jeff Sharkey48877892015-03-18 11:27:19 -07002091 enforcePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
San Mehat207e5382010-02-04 20:46:54 -08002092 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08002093 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08002094
San Mehata181b212010-02-11 06:50:20 -08002095 synchronized (mAsecMountSet) {
2096 if (mAsecMountSet.contains(id)) {
2097 return StorageResultCode.OperationFailedStorageMounted;
2098 }
2099 }
2100
San Mehatb1043402010-02-05 08:26:50 -08002101 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08002102 try {
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07002103 mConnector.execute("asec", "mount", id, new SensitiveArg(key), ownerUid,
2104 readOnly ? "ro" : "rw");
San Mehat4270e1e2010-01-29 05:32:19 -08002105 } catch (NativeDaemonConnectorException e) {
Kenny Rootf0304622010-03-19 19:20:42 -07002106 int code = e.getCode();
2107 if (code != VoldResponseCode.OpFailedStorageBusy) {
2108 rc = StorageResultCode.OperationFailedInternalError;
2109 }
San Mehat02735bc2010-01-26 15:18:08 -08002110 }
San Mehat6cdd9c02010-02-09 14:45:20 -08002111
2112 if (rc == StorageResultCode.OperationSucceeded) {
2113 synchronized (mAsecMountSet) {
2114 mAsecMountSet.add(id);
2115 }
2116 }
San Mehat4270e1e2010-01-29 05:32:19 -08002117 return rc;
San Mehat36972292010-01-06 11:06:32 -08002118 }
2119
San Mehatd9709982010-02-18 11:43:03 -08002120 public int unmountSecureContainer(String id, boolean force) {
Jeff Sharkey48877892015-03-18 11:27:19 -07002121 enforcePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
San Mehat207e5382010-02-04 20:46:54 -08002122 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08002123 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08002124
San Mehat6cdd9c02010-02-09 14:45:20 -08002125 synchronized (mAsecMountSet) {
2126 if (!mAsecMountSet.contains(id)) {
San Mehata181b212010-02-11 06:50:20 -08002127 return StorageResultCode.OperationFailedStorageNotMounted;
San Mehat6cdd9c02010-02-09 14:45:20 -08002128 }
2129 }
2130
Kenny Rootaa485402010-09-14 14:49:41 -07002131 /*
2132 * Force a GC to make sure AssetManagers in other threads of the
2133 * system_server are cleaned up. We have to do this since AssetManager
2134 * instances are kept as a WeakReference and it's possible we have files
2135 * open on the external storage.
2136 */
2137 Runtime.getRuntime().gc();
2138
San Mehatb1043402010-02-05 08:26:50 -08002139 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08002140 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002141 final Command cmd = new Command("asec", "unmount", id);
2142 if (force) {
2143 cmd.appendArg("force");
2144 }
2145 mConnector.execute(cmd);
San Mehat4270e1e2010-01-29 05:32:19 -08002146 } catch (NativeDaemonConnectorException e) {
San Mehatd9709982010-02-18 11:43:03 -08002147 int code = e.getCode();
2148 if (code == VoldResponseCode.OpFailedStorageBusy) {
2149 rc = StorageResultCode.OperationFailedStorageBusy;
2150 } else {
2151 rc = StorageResultCode.OperationFailedInternalError;
2152 }
San Mehat02735bc2010-01-26 15:18:08 -08002153 }
San Mehat6cdd9c02010-02-09 14:45:20 -08002154
2155 if (rc == StorageResultCode.OperationSucceeded) {
2156 synchronized (mAsecMountSet) {
2157 mAsecMountSet.remove(id);
2158 }
2159 }
San Mehat4270e1e2010-01-29 05:32:19 -08002160 return rc;
San Mehat9dba7092010-01-18 06:47:41 -08002161 }
2162
San Mehat6cdd9c02010-02-09 14:45:20 -08002163 public boolean isSecureContainerMounted(String id) {
Jeff Sharkey48877892015-03-18 11:27:19 -07002164 enforcePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat6cdd9c02010-02-09 14:45:20 -08002165 waitForReady();
2166 warnOnNotMounted();
2167
2168 synchronized (mAsecMountSet) {
2169 return mAsecMountSet.contains(id);
2170 }
2171 }
2172
San Mehat4270e1e2010-01-29 05:32:19 -08002173 public int renameSecureContainer(String oldId, String newId) {
Jeff Sharkey48877892015-03-18 11:27:19 -07002174 enforcePermission(android.Manifest.permission.ASEC_RENAME);
San Mehat207e5382010-02-04 20:46:54 -08002175 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08002176 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08002177
San Mehata181b212010-02-11 06:50:20 -08002178 synchronized (mAsecMountSet) {
San Mehat85451ee2010-02-24 08:54:18 -08002179 /*
Jason parks9ed98bc2011-01-17 09:58:35 -06002180 * Because a mounted container has active internal state which cannot be
San Mehat85451ee2010-02-24 08:54:18 -08002181 * changed while active, we must ensure both ids are not currently mounted.
2182 */
2183 if (mAsecMountSet.contains(oldId) || mAsecMountSet.contains(newId)) {
San Mehata181b212010-02-11 06:50:20 -08002184 return StorageResultCode.OperationFailedStorageMounted;
2185 }
2186 }
2187
San Mehatb1043402010-02-05 08:26:50 -08002188 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08002189 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002190 mConnector.execute("asec", "rename", oldId, newId);
San Mehat4270e1e2010-01-29 05:32:19 -08002191 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08002192 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08002193 }
San Mehata181b212010-02-11 06:50:20 -08002194
San Mehat4270e1e2010-01-29 05:32:19 -08002195 return rc;
San Mehat45f61042010-01-23 08:12:43 -08002196 }
2197
San Mehat4270e1e2010-01-29 05:32:19 -08002198 public String getSecureContainerPath(String id) {
Jeff Sharkey48877892015-03-18 11:27:19 -07002199 enforcePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat207e5382010-02-04 20:46:54 -08002200 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08002201 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08002202
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002203 final NativeDaemonEvent event;
San Mehat2d66cef2010-03-23 11:12:52 -07002204 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002205 event = mConnector.execute("asec", "path", id);
2206 event.checkCode(VoldResponseCode.AsecPathResult);
2207 return event.getMessage();
San Mehat2d66cef2010-03-23 11:12:52 -07002208 } catch (NativeDaemonConnectorException e) {
2209 int code = e.getCode();
2210 if (code == VoldResponseCode.OpFailedStorageNotFound) {
Fredrik Helmera20c8ef2011-02-09 16:16:10 +01002211 Slog.i(TAG, String.format("Container '%s' not found", id));
2212 return null;
San Mehat22dd86e2010-01-12 12:21:18 -08002213 } else {
San Mehat2d66cef2010-03-23 11:12:52 -07002214 throw new IllegalStateException(String.format("Unexpected response code %d", code));
San Mehat22dd86e2010-01-12 12:21:18 -08002215 }
2216 }
San Mehat22dd86e2010-01-12 12:21:18 -08002217 }
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -07002218
Dianne Hackborn292f8bc2011-06-27 16:27:41 -07002219 public String getSecureContainerFilesystemPath(String id) {
Jeff Sharkey48877892015-03-18 11:27:19 -07002220 enforcePermission(android.Manifest.permission.ASEC_ACCESS);
Dianne Hackborn292f8bc2011-06-27 16:27:41 -07002221 waitForReady();
2222 warnOnNotMounted();
2223
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002224 final NativeDaemonEvent event;
Dianne Hackborn292f8bc2011-06-27 16:27:41 -07002225 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002226 event = mConnector.execute("asec", "fspath", id);
2227 event.checkCode(VoldResponseCode.AsecPathResult);
2228 return event.getMessage();
Dianne Hackborn292f8bc2011-06-27 16:27:41 -07002229 } catch (NativeDaemonConnectorException e) {
2230 int code = e.getCode();
2231 if (code == VoldResponseCode.OpFailedStorageNotFound) {
2232 Slog.i(TAG, String.format("Container '%s' not found", id));
2233 return null;
2234 } else {
2235 throw new IllegalStateException(String.format("Unexpected response code %d", code));
2236 }
2237 }
2238 }
2239
Jeff Sharkey48877892015-03-18 11:27:19 -07002240 @Override
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -07002241 public void finishMediaUpdate() {
Jeff Sharkey9527b222015-06-24 15:24:48 -07002242 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
Rubin Xucd7a0142015-04-17 23:45:27 +01002243 throw new SecurityException("no permission to call finishMediaUpdate()");
2244 }
Jeff Sharkey48877892015-03-18 11:27:19 -07002245 if (mUnmountSignal != null) {
2246 mUnmountSignal.countDown();
2247 } else {
2248 Slog.w(TAG, "Odd, nobody asked to unmount?");
2249 }
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -07002250 }
Kenny Root02c87302010-07-01 08:10:18 -07002251
Kenny Roota02b8b02010-08-05 16:14:17 -07002252 private boolean isUidOwnerOfPackageOrSystem(String packageName, int callerUid) {
2253 if (callerUid == android.os.Process.SYSTEM_UID) {
2254 return true;
2255 }
2256
Kenny Root02c87302010-07-01 08:10:18 -07002257 if (packageName == null) {
2258 return false;
2259 }
2260
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07002261 final int packageUid = mPms.getPackageUid(packageName, UserHandle.getUserId(callerUid));
Kenny Root02c87302010-07-01 08:10:18 -07002262
2263 if (DEBUG_OBB) {
2264 Slog.d(TAG, "packageName = " + packageName + ", packageUid = " +
2265 packageUid + ", callerUid = " + callerUid);
2266 }
2267
2268 return callerUid == packageUid;
2269 }
2270
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002271 public String getMountedObbPath(String rawPath) {
2272 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
Kenny Rootaf9d6672010-10-08 09:21:39 -07002273
Kenny Root02c87302010-07-01 08:10:18 -07002274 waitForReady();
2275 warnOnNotMounted();
2276
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002277 final ObbState state;
Rubin Xucd7a0142015-04-17 23:45:27 +01002278 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002279 state = mObbPathToStateMap.get(rawPath);
2280 }
2281 if (state == null) {
2282 Slog.w(TAG, "Failed to find OBB mounted at " + rawPath);
2283 return null;
2284 }
2285
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002286 final NativeDaemonEvent event;
Kenny Root02c87302010-07-01 08:10:18 -07002287 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002288 event = mConnector.execute("obb", "path", state.voldPath);
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002289 event.checkCode(VoldResponseCode.AsecPathResult);
2290 return event.getMessage();
Kenny Root02c87302010-07-01 08:10:18 -07002291 } catch (NativeDaemonConnectorException e) {
2292 int code = e.getCode();
2293 if (code == VoldResponseCode.OpFailedStorageNotFound) {
Kenny Roota02b8b02010-08-05 16:14:17 -07002294 return null;
Kenny Root02c87302010-07-01 08:10:18 -07002295 } else {
2296 throw new IllegalStateException(String.format("Unexpected response code %d", code));
2297 }
2298 }
2299 }
2300
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002301 @Override
2302 public boolean isObbMounted(String rawPath) {
2303 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
Kenny Rootaf9d6672010-10-08 09:21:39 -07002304 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002305 return mObbPathToStateMap.containsKey(rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002306 }
Kenny Root02c87302010-07-01 08:10:18 -07002307 }
2308
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002309 @Override
2310 public void mountObb(
2311 String rawPath, String canonicalPath, String key, IObbActionListener token, int nonce) {
2312 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
2313 Preconditions.checkNotNull(canonicalPath, "canonicalPath cannot be null");
2314 Preconditions.checkNotNull(token, "token cannot be null");
Kenny Rootaf9d6672010-10-08 09:21:39 -07002315
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002316 final int callingUid = Binder.getCallingUid();
2317 final ObbState obbState = new ObbState(rawPath, canonicalPath, callingUid, token, nonce);
2318 final ObbAction action = new MountObbAction(obbState, key, callingUid);
Kenny Roota02b8b02010-08-05 16:14:17 -07002319 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
2320
2321 if (DEBUG_OBB)
2322 Slog.i(TAG, "Send to OBB handler: " + action.toString());
Kenny Root02c87302010-07-01 08:10:18 -07002323 }
2324
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002325 @Override
2326 public void unmountObb(String rawPath, boolean force, IObbActionListener token, int nonce) {
2327 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
2328
2329 final ObbState existingState;
Rubin Xucd7a0142015-04-17 23:45:27 +01002330 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002331 existingState = mObbPathToStateMap.get(rawPath);
Kenny Rootf1121dc2010-09-29 07:30:53 -07002332 }
2333
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002334 if (existingState != null) {
2335 // TODO: separate state object from request data
2336 final int callingUid = Binder.getCallingUid();
2337 final ObbState newState = new ObbState(
2338 rawPath, existingState.canonicalPath, callingUid, token, nonce);
2339 final ObbAction action = new UnmountObbAction(newState, force);
2340 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
Kenny Root02c87302010-07-01 08:10:18 -07002341
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002342 if (DEBUG_OBB)
2343 Slog.i(TAG, "Send to OBB handler: " + action.toString());
2344 } else {
2345 Slog.w(TAG, "Unknown OBB mount at " + rawPath);
2346 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002347 }
2348
Ben Komalo444eca22011-09-01 15:17:44 -07002349 @Override
2350 public int getEncryptionState() {
2351 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2352 "no permission to access the crypt keeper");
2353
2354 waitForReady();
2355
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002356 final NativeDaemonEvent event;
Ben Komalo444eca22011-09-01 15:17:44 -07002357 try {
Paul Lawrence1c62cbb2015-06-03 14:14:52 -07002358 event = mCryptConnector.execute("cryptfs", "cryptocomplete");
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002359 return Integer.parseInt(event.getMessage());
Ben Komalo444eca22011-09-01 15:17:44 -07002360 } catch (NumberFormatException e) {
2361 // Bad result - unexpected.
2362 Slog.w(TAG, "Unable to parse result from cryptfs cryptocomplete");
2363 return ENCRYPTION_STATE_ERROR_UNKNOWN;
2364 } catch (NativeDaemonConnectorException e) {
2365 // Something bad happened.
2366 Slog.w(TAG, "Error in communicating with cryptfs in validating");
2367 return ENCRYPTION_STATE_ERROR_UNKNOWN;
2368 }
2369 }
2370
2371 @Override
Jason parks5af0b912010-11-29 09:05:25 -06002372 public int decryptStorage(String password) {
Jason parksf7b3cd42011-01-27 09:28:25 -06002373 if (TextUtils.isEmpty(password)) {
2374 throw new IllegalArgumentException("password cannot be empty");
Jason parks5af0b912010-11-29 09:05:25 -06002375 }
2376
Jason parks8888c592011-01-20 22:46:41 -06002377 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2378 "no permission to access the crypt keeper");
Jason parks5af0b912010-11-29 09:05:25 -06002379
2380 waitForReady();
2381
2382 if (DEBUG_EVENTS) {
2383 Slog.i(TAG, "decrypting storage...");
2384 }
2385
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002386 final NativeDaemonEvent event;
Jason parks5af0b912010-11-29 09:05:25 -06002387 try {
Paul Lawrence05487612015-06-09 13:35:38 -07002388 event = mCryptConnector.execute("cryptfs", "checkpw", new SensitiveArg(password));
Jason parks9ed98bc2011-01-17 09:58:35 -06002389
Fredrik Roubertda6aedf2011-12-20 17:34:43 +01002390 final int code = Integer.parseInt(event.getMessage());
Jason parks9ed98bc2011-01-17 09:58:35 -06002391 if (code == 0) {
2392 // Decrypt was successful. Post a delayed message before restarting in order
2393 // to let the UI to clear itself
2394 mHandler.postDelayed(new Runnable() {
2395 public void run() {
Jeff Sharkey31c6e482011-11-18 17:09:01 -08002396 try {
Paul Lawrence1c62cbb2015-06-03 14:14:52 -07002397 mCryptConnector.execute("cryptfs", "restart");
Jeff Sharkey31c6e482011-11-18 17:09:01 -08002398 } catch (NativeDaemonConnectorException e) {
2399 Slog.e(TAG, "problem executing in background", e);
2400 }
Jason parks9ed98bc2011-01-17 09:58:35 -06002401 }
Jason parksf7b3cd42011-01-27 09:28:25 -06002402 }, 1000); // 1 second
Jason parks9ed98bc2011-01-17 09:58:35 -06002403 }
2404
2405 return code;
Jason parks5af0b912010-11-29 09:05:25 -06002406 } catch (NativeDaemonConnectorException e) {
2407 // Decryption failed
2408 return e.getCode();
2409 }
Jason parks5af0b912010-11-29 09:05:25 -06002410 }
2411
Paul Lawrence46791e72014-04-03 09:10:26 -07002412 public int encryptStorage(int type, String password) {
2413 if (TextUtils.isEmpty(password) && type != StorageManager.CRYPT_TYPE_DEFAULT) {
Jason parksf7b3cd42011-01-27 09:28:25 -06002414 throw new IllegalArgumentException("password cannot be empty");
Jason parks56aa5322011-01-07 09:01:15 -06002415 }
2416
Jason parks8888c592011-01-20 22:46:41 -06002417 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2418 "no permission to access the crypt keeper");
Jason parks56aa5322011-01-07 09:01:15 -06002419
2420 waitForReady();
2421
2422 if (DEBUG_EVENTS) {
Jason parks8888c592011-01-20 22:46:41 -06002423 Slog.i(TAG, "encrypting storage...");
Jason parks56aa5322011-01-07 09:01:15 -06002424 }
2425
2426 try {
Paul Lawrence1c62cbb2015-06-03 14:14:52 -07002427 mCryptConnector.execute("cryptfs", "enablecrypto", "inplace", CRYPTO_TYPES[type],
Paul Lawrence05487612015-06-09 13:35:38 -07002428 new SensitiveArg(password));
Jason parks56aa5322011-01-07 09:01:15 -06002429 } catch (NativeDaemonConnectorException e) {
2430 // Encryption failed
2431 return e.getCode();
2432 }
2433
2434 return 0;
2435 }
2436
Paul Lawrence8e397362014-01-27 15:22:30 -08002437 /** Set the password for encrypting the master key.
2438 * @param type One of the CRYPTO_TYPE_XXX consts defined in StorageManager.
2439 * @param password The password to set.
2440 */
2441 public int changeEncryptionPassword(int type, String password) {
Jason parksf7b3cd42011-01-27 09:28:25 -06002442 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2443 "no permission to access the crypt keeper");
2444
2445 waitForReady();
2446
2447 if (DEBUG_EVENTS) {
2448 Slog.i(TAG, "changing encryption password...");
2449 }
2450
2451 try {
Paul Lawrence1c62cbb2015-06-03 14:14:52 -07002452 NativeDaemonEvent event = mCryptConnector.execute("cryptfs", "changepw", CRYPTO_TYPES[type],
Paul Lawrence05487612015-06-09 13:35:38 -07002453 new SensitiveArg(password));
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002454 return Integer.parseInt(event.getMessage());
Jason parksf7b3cd42011-01-27 09:28:25 -06002455 } catch (NativeDaemonConnectorException e) {
2456 // Encryption failed
2457 return e.getCode();
2458 }
2459 }
2460
Christopher Tate32418be2011-10-10 13:51:12 -07002461 /**
2462 * Validate a user-supplied password string with cryptfs
2463 */
2464 @Override
2465 public int verifyEncryptionPassword(String password) throws RemoteException {
2466 // Only the system process is permitted to validate passwords
2467 if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
2468 throw new SecurityException("no permission to access the crypt keeper");
2469 }
2470
2471 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2472 "no permission to access the crypt keeper");
2473
2474 if (TextUtils.isEmpty(password)) {
2475 throw new IllegalArgumentException("password cannot be empty");
2476 }
2477
2478 waitForReady();
2479
2480 if (DEBUG_EVENTS) {
2481 Slog.i(TAG, "validating encryption password...");
2482 }
2483
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002484 final NativeDaemonEvent event;
Christopher Tate32418be2011-10-10 13:51:12 -07002485 try {
Paul Lawrence05487612015-06-09 13:35:38 -07002486 event = mCryptConnector.execute("cryptfs", "verifypw", new SensitiveArg(password));
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002487 Slog.i(TAG, "cryptfs verifypw => " + event.getMessage());
2488 return Integer.parseInt(event.getMessage());
Christopher Tate32418be2011-10-10 13:51:12 -07002489 } catch (NativeDaemonConnectorException e) {
2490 // Encryption failed
2491 return e.getCode();
2492 }
2493 }
2494
Paul Lawrence8e397362014-01-27 15:22:30 -08002495 /**
2496 * Get the type of encryption used to encrypt the master key.
2497 * @return The type, one of the CRYPT_TYPE_XXX consts from StorageManager.
2498 */
2499 @Override
Svetoslav16e4a1a2014-09-29 18:16:20 -07002500 public int getPasswordType() {
Paul Lawrence8e397362014-01-27 15:22:30 -08002501
2502 waitForReady();
2503
2504 final NativeDaemonEvent event;
2505 try {
Paul Lawrence1c62cbb2015-06-03 14:14:52 -07002506 event = mCryptConnector.execute("cryptfs", "getpwtype");
Paul Lawrence8e397362014-01-27 15:22:30 -08002507 for (int i = 0; i < CRYPTO_TYPES.length; ++i) {
2508 if (CRYPTO_TYPES[i].equals(event.getMessage()))
2509 return i;
2510 }
2511
2512 throw new IllegalStateException("unexpected return from cryptfs");
2513 } catch (NativeDaemonConnectorException e) {
2514 throw e.rethrowAsParcelableException();
2515 }
2516 }
2517
Paul Lawrencee51dcf92014-03-18 10:56:00 -07002518 /**
2519 * Set a field in the crypto header.
2520 * @param field field to set
2521 * @param contents contents to set in field
2522 */
2523 @Override
2524 public void setField(String field, String contents) throws RemoteException {
2525
2526 waitForReady();
2527
2528 final NativeDaemonEvent event;
2529 try {
Paul Lawrence1c62cbb2015-06-03 14:14:52 -07002530 event = mCryptConnector.execute("cryptfs", "setfield", field, contents);
Paul Lawrencee51dcf92014-03-18 10:56:00 -07002531 } catch (NativeDaemonConnectorException e) {
2532 throw e.rethrowAsParcelableException();
2533 }
2534 }
2535
2536 /**
2537 * Gets a field from the crypto header.
2538 * @param field field to get
2539 * @return contents of field
2540 */
2541 @Override
2542 public String getField(String field) throws RemoteException {
2543
2544 waitForReady();
2545
2546 final NativeDaemonEvent event;
2547 try {
2548 final String[] contents = NativeDaemonEvent.filterMessageList(
Paul Lawrence1c62cbb2015-06-03 14:14:52 -07002549 mCryptConnector.executeForList("cryptfs", "getfield", field),
Paul Lawrencee51dcf92014-03-18 10:56:00 -07002550 VoldResponseCode.CryptfsGetfieldResult);
2551 String result = new String();
2552 for (String content : contents) {
2553 result += content;
2554 }
2555 return result;
2556 } catch (NativeDaemonConnectorException e) {
2557 throw e.rethrowAsParcelableException();
2558 }
2559 }
2560
Jeff Sharkeyb049e212012-09-07 23:16:01 -07002561 @Override
Paul Lawrence945490c2014-03-27 16:37:28 +00002562 public String getPassword() throws RemoteException {
Rubin Xucd7a0142015-04-17 23:45:27 +01002563 mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE,
2564 "only keyguard can retrieve password");
Paul Lawrence945490c2014-03-27 16:37:28 +00002565 if (!isReady()) {
2566 return new String();
2567 }
2568
2569 final NativeDaemonEvent event;
2570 try {
Paul Lawrence1c62cbb2015-06-03 14:14:52 -07002571 event = mCryptConnector.execute("cryptfs", "getpw");
Paul Lawrence24063b52015-01-06 13:11:23 -08002572 if ("-1".equals(event.getMessage())) {
2573 // -1 equals no password
2574 return null;
2575 }
Paul Lawrence05487612015-06-09 13:35:38 -07002576 return event.getMessage();
Paul Lawrence945490c2014-03-27 16:37:28 +00002577 } catch (NativeDaemonConnectorException e) {
2578 throw e.rethrowAsParcelableException();
Paul Lawrence24063b52015-01-06 13:11:23 -08002579 } catch (IllegalArgumentException e) {
2580 Slog.e(TAG, "Invalid response to getPassword");
2581 return null;
Paul Lawrence945490c2014-03-27 16:37:28 +00002582 }
2583 }
2584
2585 @Override
2586 public void clearPassword() throws RemoteException {
2587 if (!isReady()) {
2588 return;
2589 }
2590
2591 final NativeDaemonEvent event;
2592 try {
Paul Lawrence1c62cbb2015-06-03 14:14:52 -07002593 event = mCryptConnector.execute("cryptfs", "clearpw");
Paul Lawrence945490c2014-03-27 16:37:28 +00002594 } catch (NativeDaemonConnectorException e) {
2595 throw e.rethrowAsParcelableException();
2596 }
2597 }
2598
2599 @Override
Paul Crowleybcf48ed2015-04-22 13:36:59 +01002600 public void createNewUserDir(int userHandle, String path) {
2601 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
2602 throw new SecurityException("Only SYSTEM_UID can create user directories");
2603 }
2604
2605 waitForReady();
2606
2607 if (DEBUG_EVENTS) {
2608 Slog.i(TAG, "Creating new user dir");
2609 }
2610
2611 try {
2612 NativeDaemonEvent event = mCryptConnector.execute(
2613 "cryptfs", "createnewuserdir", userHandle, path);
2614 if (!"0".equals(event.getMessage())) {
2615 String error = "createnewuserdir sent unexpected message: "
2616 + event.getMessage();
2617 Slog.e(TAG, error);
2618 // ext4enc:TODO is this the right exception?
2619 throw new RuntimeException(error);
2620 }
2621 } catch (NativeDaemonConnectorException e) {
2622 Slog.e(TAG, "createnewuserdir threw exception", e);
2623 throw new RuntimeException("createnewuserdir threw exception", e);
2624 }
2625 }
2626
Paul Crowley7ec733f2015-05-19 12:42:00 +01002627 // ext4enc:TODO duplication between this and createNewUserDir is nasty
2628 @Override
2629 public void deleteUserKey(int userHandle) {
2630 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
2631 throw new SecurityException("Only SYSTEM_UID can delete user keys");
2632 }
2633
2634 waitForReady();
2635
2636 if (DEBUG_EVENTS) {
2637 Slog.i(TAG, "Deleting user key");
2638 }
2639
2640 try {
2641 NativeDaemonEvent event = mCryptConnector.execute(
2642 "cryptfs", "deleteuserkey", userHandle);
2643 if (!"0".equals(event.getMessage())) {
2644 String error = "deleteuserkey sent unexpected message: "
2645 + event.getMessage();
2646 Slog.e(TAG, error);
2647 // ext4enc:TODO is this the right exception?
2648 throw new RuntimeException(error);
2649 }
2650 } catch (NativeDaemonConnectorException e) {
2651 Slog.e(TAG, "deleteuserkey threw exception", e);
2652 throw new RuntimeException("deleteuserkey threw exception", e);
2653 }
2654 }
2655
Paul Crowleybcf48ed2015-04-22 13:36:59 +01002656 @Override
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002657 public int mkdirs(String callingPkg, String appPath) {
2658 final int userId = UserHandle.getUserId(Binder.getCallingUid());
2659 final UserEnvironment userEnv = new UserEnvironment(userId);
2660
2661 // Validate that reported package name belongs to caller
2662 final AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(
2663 Context.APP_OPS_SERVICE);
2664 appOps.checkPackage(Binder.getCallingUid(), callingPkg);
2665
Jeff Sharkey48877892015-03-18 11:27:19 -07002666 File appFile = null;
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002667 try {
Jeff Sharkey48877892015-03-18 11:27:19 -07002668 appFile = new File(appPath).getCanonicalFile();
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002669 } catch (IOException e) {
2670 Slog.e(TAG, "Failed to resolve " + appPath + ": " + e);
2671 return -1;
2672 }
2673
2674 // Try translating the app path into a vold path, but require that it
2675 // belong to the calling package.
Jeff Sharkey48877892015-03-18 11:27:19 -07002676 if (FileUtils.contains(userEnv.buildExternalStorageAppDataDirs(callingPkg), appFile) ||
2677 FileUtils.contains(userEnv.buildExternalStorageAppObbDirs(callingPkg), appFile) ||
2678 FileUtils.contains(userEnv.buildExternalStorageAppMediaDirs(callingPkg), appFile)) {
2679 appPath = appFile.getAbsolutePath();
2680 if (!appPath.endsWith("/")) {
2681 appPath = appPath + "/";
2682 }
2683
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002684 try {
Jeff Sharkey48877892015-03-18 11:27:19 -07002685 mConnector.execute("volume", "mkdirs", appPath);
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002686 return 0;
2687 } catch (NativeDaemonConnectorException e) {
2688 return e.getCode();
2689 }
2690 }
2691
Jeff Sharkey48877892015-03-18 11:27:19 -07002692 throw new SecurityException("Invalid mkdirs path: " + appFile);
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002693 }
2694
2695 @Override
Jeff Sharkey46349872015-07-28 10:49:47 -07002696 public StorageVolume[] getVolumeList(int uid, String packageName, int flags) {
2697 final boolean forWrite = (flags & StorageManager.FLAG_FOR_WRITE) != 0;
2698
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002699 final ArrayList<StorageVolume> res = new ArrayList<>();
Jeff Sharkey48877892015-03-18 11:27:19 -07002700 boolean foundPrimary = false;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002701
Svet Ganov6ee871e2015-07-10 14:29:33 -07002702 final int userId = UserHandle.getUserId(uid);
Svetoslav38c3dbb2015-07-14 11:27:06 -07002703 final boolean reportUnmounted;
Svetoslav38c3dbb2015-07-14 11:27:06 -07002704 final long identity = Binder.clearCallingIdentity();
2705 try {
2706 reportUnmounted = !mMountServiceInternal.hasExternalStorage(
2707 uid, packageName);
2708 } finally {
2709 Binder.restoreCallingIdentity(identity);
2710 }
Svet Ganov6ee871e2015-07-10 14:29:33 -07002711
Jeff Sharkey48877892015-03-18 11:27:19 -07002712 synchronized (mLock) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002713 for (int i = 0; i < mVolumes.size(); i++) {
2714 final VolumeInfo vol = mVolumes.valueAt(i);
Jeff Sharkey46349872015-07-28 10:49:47 -07002715 if (forWrite ? vol.isVisibleForWrite(userId) : vol.isVisibleForRead(userId)) {
Svet Ganov6ee871e2015-07-10 14:29:33 -07002716 final StorageVolume userVol = vol.buildStorageVolume(mContext, userId,
2717 reportUnmounted);
Jeff Sharkey48877892015-03-18 11:27:19 -07002718 if (vol.isPrimary()) {
2719 res.add(0, userVol);
2720 foundPrimary = true;
2721 } else {
2722 res.add(userVol);
2723 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -07002724 }
Mike Lockwood8fa5f802011-03-24 08:12:30 -07002725 }
Mike Lockwood8fa5f802011-03-24 08:12:30 -07002726 }
Jeff Sharkey48877892015-03-18 11:27:19 -07002727
2728 if (!foundPrimary) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002729 Log.w(TAG, "No primary storage defined yet; hacking together a stub");
Jeff Sharkey48877892015-03-18 11:27:19 -07002730
2731 final boolean primaryPhysical = SystemProperties.getBoolean(
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002732 StorageManager.PROP_PRIMARY_PHYSICAL, false);
Jeff Sharkey48877892015-03-18 11:27:19 -07002733
2734 final String id = "stub_primary";
2735 final File path = Environment.getLegacyExternalStorageDirectory();
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002736 final String description = mContext.getString(android.R.string.unknownName);
Jeff Sharkey48877892015-03-18 11:27:19 -07002737 final boolean primary = true;
2738 final boolean removable = primaryPhysical;
2739 final boolean emulated = !primaryPhysical;
2740 final long mtpReserveSize = 0L;
2741 final boolean allowMassStorage = false;
2742 final long maxFileSize = 0L;
2743 final UserHandle owner = new UserHandle(userId);
2744 final String uuid = null;
Jeff Sharkey48877892015-03-18 11:27:19 -07002745 final String state = Environment.MEDIA_REMOVED;
2746
Jeff Sharkey5af1835d2015-07-07 17:26:59 -07002747 res.add(0, new StorageVolume(id, StorageVolume.STORAGE_ID_INVALID, path,
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002748 description, primary, removable, emulated, mtpReserveSize,
2749 allowMassStorage, maxFileSize, owner, uuid, state));
Jeff Sharkey48877892015-03-18 11:27:19 -07002750 }
2751
2752 return res.toArray(new StorageVolume[res.size()]);
Mike Lockwood8fa5f802011-03-24 08:12:30 -07002753 }
2754
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002755 @Override
2756 public DiskInfo[] getDisks() {
2757 synchronized (mLock) {
2758 final DiskInfo[] res = new DiskInfo[mDisks.size()];
2759 for (int i = 0; i < mDisks.size(); i++) {
2760 res[i] = mDisks.valueAt(i);
2761 }
2762 return res;
2763 }
2764 }
2765
2766 @Override
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07002767 public VolumeInfo[] getVolumes(int flags) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002768 synchronized (mLock) {
2769 final VolumeInfo[] res = new VolumeInfo[mVolumes.size()];
2770 for (int i = 0; i < mVolumes.size(); i++) {
2771 res[i] = mVolumes.valueAt(i);
2772 }
2773 return res;
2774 }
2775 }
2776
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07002777 @Override
2778 public VolumeRecord[] getVolumeRecords(int flags) {
2779 synchronized (mLock) {
2780 final VolumeRecord[] res = new VolumeRecord[mRecords.size()];
2781 for (int i = 0; i < mRecords.size(); i++) {
2782 res[i] = mRecords.valueAt(i);
2783 }
2784 return res;
2785 }
2786 }
2787
Kenny Rootaf9d6672010-10-08 09:21:39 -07002788 private void addObbStateLocked(ObbState obbState) throws RemoteException {
2789 final IBinder binder = obbState.getBinder();
2790 List<ObbState> obbStates = mObbMounts.get(binder);
Kenny Root5919ac62010-10-05 09:49:40 -07002791
Kenny Rootaf9d6672010-10-08 09:21:39 -07002792 if (obbStates == null) {
2793 obbStates = new ArrayList<ObbState>();
2794 mObbMounts.put(binder, obbStates);
2795 } else {
2796 for (final ObbState o : obbStates) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002797 if (o.rawPath.equals(obbState.rawPath)) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002798 throw new IllegalStateException("Attempt to add ObbState twice. "
2799 + "This indicates an error in the MountService logic.");
Kenny Root5919ac62010-10-05 09:49:40 -07002800 }
2801 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002802 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07002803
2804 obbStates.add(obbState);
2805 try {
2806 obbState.link();
2807 } catch (RemoteException e) {
2808 /*
2809 * The binder died before we could link it, so clean up our state
2810 * and return failure.
2811 */
2812 obbStates.remove(obbState);
2813 if (obbStates.isEmpty()) {
2814 mObbMounts.remove(binder);
2815 }
2816
2817 // Rethrow the error so mountObb can get it
2818 throw e;
2819 }
2820
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002821 mObbPathToStateMap.put(obbState.rawPath, obbState);
Kenny Roota02b8b02010-08-05 16:14:17 -07002822 }
2823
Kenny Rootaf9d6672010-10-08 09:21:39 -07002824 private void removeObbStateLocked(ObbState obbState) {
2825 final IBinder binder = obbState.getBinder();
2826 final List<ObbState> obbStates = mObbMounts.get(binder);
2827 if (obbStates != null) {
2828 if (obbStates.remove(obbState)) {
2829 obbState.unlink();
Kenny Root05105f72010-09-22 17:29:43 -07002830 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07002831 if (obbStates.isEmpty()) {
2832 mObbMounts.remove(binder);
2833 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002834 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002835
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002836 mObbPathToStateMap.remove(obbState.rawPath);
Kenny Root38cf8862010-09-26 14:18:51 -07002837 }
2838
Kenny Roota02b8b02010-08-05 16:14:17 -07002839 private class ObbActionHandler extends Handler {
2840 private boolean mBound = false;
Kenny Root480afe72010-10-07 10:17:50 -07002841 private final List<ObbAction> mActions = new LinkedList<ObbAction>();
Kenny Roota02b8b02010-08-05 16:14:17 -07002842
2843 ObbActionHandler(Looper l) {
2844 super(l);
2845 }
2846
2847 @Override
2848 public void handleMessage(Message msg) {
2849 switch (msg.what) {
2850 case OBB_RUN_ACTION: {
Kenny Root480afe72010-10-07 10:17:50 -07002851 final ObbAction action = (ObbAction) msg.obj;
Kenny Roota02b8b02010-08-05 16:14:17 -07002852
2853 if (DEBUG_OBB)
2854 Slog.i(TAG, "OBB_RUN_ACTION: " + action.toString());
2855
2856 // If a bind was already initiated we don't really
2857 // need to do anything. The pending install
2858 // will be processed later on.
2859 if (!mBound) {
2860 // If this is the only one pending we might
2861 // have to bind to the service again.
2862 if (!connectToService()) {
2863 Slog.e(TAG, "Failed to bind to media container service");
2864 action.handleError();
2865 return;
Kenny Roota02b8b02010-08-05 16:14:17 -07002866 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002867 }
Kenny Root735de3b2010-09-30 14:11:39 -07002868
Kenny Root735de3b2010-09-30 14:11:39 -07002869 mActions.add(action);
Kenny Roota02b8b02010-08-05 16:14:17 -07002870 break;
2871 }
2872 case OBB_MCS_BOUND: {
2873 if (DEBUG_OBB)
2874 Slog.i(TAG, "OBB_MCS_BOUND");
2875 if (msg.obj != null) {
2876 mContainerService = (IMediaContainerService) msg.obj;
2877 }
2878 if (mContainerService == null) {
2879 // Something seriously wrong. Bail out
2880 Slog.e(TAG, "Cannot bind to media container service");
2881 for (ObbAction action : mActions) {
2882 // Indicate service bind error
2883 action.handleError();
2884 }
2885 mActions.clear();
2886 } else if (mActions.size() > 0) {
Kenny Root480afe72010-10-07 10:17:50 -07002887 final ObbAction action = mActions.get(0);
Kenny Roota02b8b02010-08-05 16:14:17 -07002888 if (action != null) {
2889 action.execute(this);
2890 }
2891 } else {
2892 // Should never happen ideally.
2893 Slog.w(TAG, "Empty queue");
2894 }
2895 break;
2896 }
2897 case OBB_MCS_RECONNECT: {
2898 if (DEBUG_OBB)
2899 Slog.i(TAG, "OBB_MCS_RECONNECT");
2900 if (mActions.size() > 0) {
2901 if (mBound) {
2902 disconnectService();
2903 }
2904 if (!connectToService()) {
2905 Slog.e(TAG, "Failed to bind to media container service");
2906 for (ObbAction action : mActions) {
2907 // Indicate service bind error
2908 action.handleError();
2909 }
2910 mActions.clear();
2911 }
2912 }
2913 break;
2914 }
2915 case OBB_MCS_UNBIND: {
2916 if (DEBUG_OBB)
2917 Slog.i(TAG, "OBB_MCS_UNBIND");
2918
2919 // Delete pending install
2920 if (mActions.size() > 0) {
2921 mActions.remove(0);
2922 }
2923 if (mActions.size() == 0) {
2924 if (mBound) {
2925 disconnectService();
2926 }
2927 } else {
2928 // There are more pending requests in queue.
2929 // Just post MCS_BOUND message to trigger processing
2930 // of next pending install.
2931 mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND);
2932 }
2933 break;
2934 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07002935 case OBB_FLUSH_MOUNT_STATE: {
2936 final String path = (String) msg.obj;
2937
2938 if (DEBUG_OBB)
2939 Slog.i(TAG, "Flushing all OBB state for path " + path);
2940
2941 synchronized (mObbMounts) {
2942 final List<ObbState> obbStatesToRemove = new LinkedList<ObbState>();
2943
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002944 final Iterator<ObbState> i = mObbPathToStateMap.values().iterator();
Kenny Rootaf9d6672010-10-08 09:21:39 -07002945 while (i.hasNext()) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002946 final ObbState state = i.next();
Kenny Rootaf9d6672010-10-08 09:21:39 -07002947
2948 /*
2949 * If this entry's source file is in the volume path
2950 * that got unmounted, remove it because it's no
2951 * longer valid.
2952 */
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002953 if (state.canonicalPath.startsWith(path)) {
2954 obbStatesToRemove.add(state);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002955 }
2956 }
2957
2958 for (final ObbState obbState : obbStatesToRemove) {
2959 if (DEBUG_OBB)
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002960 Slog.i(TAG, "Removing state for " + obbState.rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002961
2962 removeObbStateLocked(obbState);
2963
2964 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002965 obbState.token.onObbResult(obbState.rawPath, obbState.nonce,
Kenny Rootaf9d6672010-10-08 09:21:39 -07002966 OnObbStateChangeListener.UNMOUNTED);
2967 } catch (RemoteException e) {
2968 Slog.i(TAG, "Couldn't send unmount notification for OBB: "
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002969 + obbState.rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002970 }
2971 }
2972 }
2973 break;
2974 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002975 }
2976 }
2977
2978 private boolean connectToService() {
2979 if (DEBUG_OBB)
2980 Slog.i(TAG, "Trying to bind to DefaultContainerService");
2981
2982 Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
Jeff Sharkey6dce4962015-07-03 18:08:41 -07002983 if (mContext.bindServiceAsUser(service, mDefContainerConn, Context.BIND_AUTO_CREATE,
Xiaohui Chene4de5a02015-09-22 15:33:31 -07002984 UserHandle.SYSTEM)) {
Kenny Roota02b8b02010-08-05 16:14:17 -07002985 mBound = true;
2986 return true;
2987 }
2988 return false;
2989 }
2990
2991 private void disconnectService() {
2992 mContainerService = null;
2993 mBound = false;
2994 mContext.unbindService(mDefContainerConn);
2995 }
2996 }
2997
2998 abstract class ObbAction {
2999 private static final int MAX_RETRIES = 3;
3000 private int mRetries;
3001
3002 ObbState mObbState;
3003
3004 ObbAction(ObbState obbState) {
3005 mObbState = obbState;
3006 }
3007
3008 public void execute(ObbActionHandler handler) {
3009 try {
3010 if (DEBUG_OBB)
Ben Komalo444eca22011-09-01 15:17:44 -07003011 Slog.i(TAG, "Starting to execute action: " + toString());
Kenny Roota02b8b02010-08-05 16:14:17 -07003012 mRetries++;
3013 if (mRetries > MAX_RETRIES) {
3014 Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
Kenny Root480afe72010-10-07 10:17:50 -07003015 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
Kenny Roota02b8b02010-08-05 16:14:17 -07003016 handleError();
Kenny Roota02b8b02010-08-05 16:14:17 -07003017 } else {
3018 handleExecute();
3019 if (DEBUG_OBB)
3020 Slog.i(TAG, "Posting install MCS_UNBIND");
3021 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
3022 }
3023 } catch (RemoteException e) {
3024 if (DEBUG_OBB)
3025 Slog.i(TAG, "Posting install MCS_RECONNECT");
3026 mObbActionHandler.sendEmptyMessage(OBB_MCS_RECONNECT);
3027 } catch (Exception e) {
3028 if (DEBUG_OBB)
3029 Slog.d(TAG, "Error handling OBB action", e);
3030 handleError();
Kenny Root17eb6fb2010-10-06 15:02:52 -07003031 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
Kenny Roota02b8b02010-08-05 16:14:17 -07003032 }
3033 }
3034
Kenny Root05105f72010-09-22 17:29:43 -07003035 abstract void handleExecute() throws RemoteException, IOException;
Kenny Roota02b8b02010-08-05 16:14:17 -07003036 abstract void handleError();
Kenny Root38cf8862010-09-26 14:18:51 -07003037
3038 protected ObbInfo getObbInfo() throws IOException {
3039 ObbInfo obbInfo;
3040 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003041 obbInfo = mContainerService.getObbInfo(mObbState.ownerPath);
Kenny Root38cf8862010-09-26 14:18:51 -07003042 } catch (RemoteException e) {
3043 Slog.d(TAG, "Couldn't call DefaultContainerService to fetch OBB info for "
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003044 + mObbState.ownerPath);
Kenny Root38cf8862010-09-26 14:18:51 -07003045 obbInfo = null;
3046 }
3047 if (obbInfo == null) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003048 throw new IOException("Couldn't read OBB file: " + mObbState.ownerPath);
Kenny Root38cf8862010-09-26 14:18:51 -07003049 }
3050 return obbInfo;
3051 }
3052
Kenny Rootaf9d6672010-10-08 09:21:39 -07003053 protected void sendNewStatusOrIgnore(int status) {
3054 if (mObbState == null || mObbState.token == null) {
3055 return;
3056 }
3057
Kenny Root38cf8862010-09-26 14:18:51 -07003058 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003059 mObbState.token.onObbResult(mObbState.rawPath, mObbState.nonce, status);
Kenny Root38cf8862010-09-26 14:18:51 -07003060 } catch (RemoteException e) {
3061 Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
3062 }
3063 }
Kenny Roota02b8b02010-08-05 16:14:17 -07003064 }
3065
3066 class MountObbAction extends ObbAction {
Ben Komalo444eca22011-09-01 15:17:44 -07003067 private final String mKey;
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003068 private final int mCallingUid;
Kenny Roota02b8b02010-08-05 16:14:17 -07003069
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003070 MountObbAction(ObbState obbState, String key, int callingUid) {
Kenny Roota02b8b02010-08-05 16:14:17 -07003071 super(obbState);
3072 mKey = key;
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003073 mCallingUid = callingUid;
Kenny Roota02b8b02010-08-05 16:14:17 -07003074 }
3075
Jason parks5af0b912010-11-29 09:05:25 -06003076 @Override
Kenny Root735de3b2010-09-30 14:11:39 -07003077 public void handleExecute() throws IOException, RemoteException {
Kenny Rootaf9d6672010-10-08 09:21:39 -07003078 waitForReady();
3079 warnOnNotMounted();
3080
Kenny Root38cf8862010-09-26 14:18:51 -07003081 final ObbInfo obbInfo = getObbInfo();
3082
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003083 if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mCallingUid)) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07003084 Slog.w(TAG, "Denied attempt to mount OBB " + obbInfo.filename
3085 + " which is owned by " + obbInfo.packageName);
3086 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
3087 return;
Kenny Roota02b8b02010-08-05 16:14:17 -07003088 }
3089
Kenny Rootaf9d6672010-10-08 09:21:39 -07003090 final boolean isMounted;
3091 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003092 isMounted = mObbPathToStateMap.containsKey(mObbState.rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07003093 }
3094 if (isMounted) {
3095 Slog.w(TAG, "Attempt to mount OBB which is already mounted: " + obbInfo.filename);
3096 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_ALREADY_MOUNTED);
3097 return;
3098 }
3099
Kenny Rootaf9d6672010-10-08 09:21:39 -07003100 final String hashedKey;
3101 if (mKey == null) {
3102 hashedKey = "none";
3103 } else {
Kenny Rootaf9d6672010-10-08 09:21:39 -07003104 try {
Kenny Root3b1abba2010-10-13 15:00:07 -07003105 SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
3106
3107 KeySpec ks = new PBEKeySpec(mKey.toCharArray(), obbInfo.salt,
3108 PBKDF2_HASH_ROUNDS, CRYPTO_ALGORITHM_KEY_SIZE);
3109 SecretKey key = factory.generateSecret(ks);
3110 BigInteger bi = new BigInteger(key.getEncoded());
3111 hashedKey = bi.toString(16);
Kenny Rootaf9d6672010-10-08 09:21:39 -07003112 } catch (NoSuchAlgorithmException e) {
Kenny Root3b1abba2010-10-13 15:00:07 -07003113 Slog.e(TAG, "Could not load PBKDF2 algorithm", e);
3114 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
3115 return;
3116 } catch (InvalidKeySpecException e) {
3117 Slog.e(TAG, "Invalid key spec when loading PBKDF2 algorithm", e);
3118 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
Kenny Root38cf8862010-09-26 14:18:51 -07003119 return;
3120 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07003121 }
Kenny Root38cf8862010-09-26 14:18:51 -07003122
Kenny Rootaf9d6672010-10-08 09:21:39 -07003123 int rc = StorageResultCode.OperationSucceeded;
Kenny Rootaf9d6672010-10-08 09:21:39 -07003124 try {
Jeff Sharkey56cd6462013-06-07 15:09:15 -07003125 mConnector.execute("obb", "mount", mObbState.voldPath, new SensitiveArg(hashedKey),
3126 mObbState.ownerGid);
Kenny Rootaf9d6672010-10-08 09:21:39 -07003127 } catch (NativeDaemonConnectorException e) {
3128 int code = e.getCode();
3129 if (code != VoldResponseCode.OpFailedStorageBusy) {
3130 rc = StorageResultCode.OperationFailedInternalError;
Kenny Roota02b8b02010-08-05 16:14:17 -07003131 }
3132 }
3133
Kenny Rootaf9d6672010-10-08 09:21:39 -07003134 if (rc == StorageResultCode.OperationSucceeded) {
3135 if (DEBUG_OBB)
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003136 Slog.d(TAG, "Successfully mounted OBB " + mObbState.voldPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07003137
3138 synchronized (mObbMounts) {
3139 addObbStateLocked(mObbState);
3140 }
3141
3142 sendNewStatusOrIgnore(OnObbStateChangeListener.MOUNTED);
Kenny Root02c87302010-07-01 08:10:18 -07003143 } else {
Kenny Root05105f72010-09-22 17:29:43 -07003144 Slog.e(TAG, "Couldn't mount OBB file: " + rc);
Kenny Roota02b8b02010-08-05 16:14:17 -07003145
Kenny Rootaf9d6672010-10-08 09:21:39 -07003146 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT);
Kenny Root02c87302010-07-01 08:10:18 -07003147 }
3148 }
3149
Jason parks5af0b912010-11-29 09:05:25 -06003150 @Override
Kenny Roota02b8b02010-08-05 16:14:17 -07003151 public void handleError() {
Kenny Rootaf9d6672010-10-08 09:21:39 -07003152 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
Kenny Root02c87302010-07-01 08:10:18 -07003153 }
Kenny Roota02b8b02010-08-05 16:14:17 -07003154
3155 @Override
3156 public String toString() {
3157 StringBuilder sb = new StringBuilder();
3158 sb.append("MountObbAction{");
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003159 sb.append(mObbState);
Kenny Roota02b8b02010-08-05 16:14:17 -07003160 sb.append('}');
3161 return sb.toString();
3162 }
3163 }
3164
3165 class UnmountObbAction extends ObbAction {
Ben Komalo444eca22011-09-01 15:17:44 -07003166 private final boolean mForceUnmount;
Kenny Roota02b8b02010-08-05 16:14:17 -07003167
3168 UnmountObbAction(ObbState obbState, boolean force) {
3169 super(obbState);
3170 mForceUnmount = force;
3171 }
3172
Jason parks5af0b912010-11-29 09:05:25 -06003173 @Override
Kenny Root38cf8862010-09-26 14:18:51 -07003174 public void handleExecute() throws IOException {
Kenny Rootaf9d6672010-10-08 09:21:39 -07003175 waitForReady();
3176 warnOnNotMounted();
3177
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003178 final ObbState existingState;
Kenny Root38cf8862010-09-26 14:18:51 -07003179 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003180 existingState = mObbPathToStateMap.get(mObbState.rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07003181 }
Kenny Root38cf8862010-09-26 14:18:51 -07003182
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003183 if (existingState == null) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07003184 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_NOT_MOUNTED);
3185 return;
3186 }
3187
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003188 if (existingState.ownerGid != mObbState.ownerGid) {
3189 Slog.w(TAG, "Permission denied attempting to unmount OBB " + existingState.rawPath
3190 + " (owned by GID " + existingState.ownerGid + ")");
Kenny Rootaf9d6672010-10-08 09:21:39 -07003191 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
3192 return;
3193 }
3194
Kenny Rootaf9d6672010-10-08 09:21:39 -07003195 int rc = StorageResultCode.OperationSucceeded;
Kenny Rootaf9d6672010-10-08 09:21:39 -07003196 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003197 final Command cmd = new Command("obb", "unmount", mObbState.voldPath);
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08003198 if (mForceUnmount) {
3199 cmd.appendArg("force");
3200 }
3201 mConnector.execute(cmd);
Kenny Rootaf9d6672010-10-08 09:21:39 -07003202 } catch (NativeDaemonConnectorException e) {
3203 int code = e.getCode();
3204 if (code == VoldResponseCode.OpFailedStorageBusy) {
3205 rc = StorageResultCode.OperationFailedStorageBusy;
3206 } else if (code == VoldResponseCode.OpFailedStorageNotFound) {
3207 // If it's not mounted then we've already won.
3208 rc = StorageResultCode.OperationSucceeded;
3209 } else {
3210 rc = StorageResultCode.OperationFailedInternalError;
Kenny Roota02b8b02010-08-05 16:14:17 -07003211 }
3212 }
3213
Kenny Rootaf9d6672010-10-08 09:21:39 -07003214 if (rc == StorageResultCode.OperationSucceeded) {
3215 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003216 removeObbStateLocked(existingState);
Kenny Root38cf8862010-09-26 14:18:51 -07003217 }
3218
Kenny Rootaf9d6672010-10-08 09:21:39 -07003219 sendNewStatusOrIgnore(OnObbStateChangeListener.UNMOUNTED);
Kenny Roota02b8b02010-08-05 16:14:17 -07003220 } else {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003221 Slog.w(TAG, "Could not unmount OBB: " + existingState);
Kenny Rootaf9d6672010-10-08 09:21:39 -07003222 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_UNMOUNT);
Kenny Roota02b8b02010-08-05 16:14:17 -07003223 }
3224 }
3225
Jason parks5af0b912010-11-29 09:05:25 -06003226 @Override
Kenny Roota02b8b02010-08-05 16:14:17 -07003227 public void handleError() {
Kenny Rootaf9d6672010-10-08 09:21:39 -07003228 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
Kenny Roota02b8b02010-08-05 16:14:17 -07003229 }
3230
3231 @Override
3232 public String toString() {
3233 StringBuilder sb = new StringBuilder();
3234 sb.append("UnmountObbAction{");
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003235 sb.append(mObbState);
Kenny Roota02b8b02010-08-05 16:14:17 -07003236 sb.append(",force=");
3237 sb.append(mForceUnmount);
Kenny Roota02b8b02010-08-05 16:14:17 -07003238 sb.append('}');
3239 return sb.toString();
3240 }
Kenny Root02c87302010-07-01 08:10:18 -07003241 }
Kenny Root38cf8862010-09-26 14:18:51 -07003242
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -08003243 @VisibleForTesting
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003244 public static String buildObbPath(final String canonicalPath, int userId, boolean forVold) {
3245 // TODO: allow caller to provide Environment for full testing
Jeff Sharkey1abdb712013-08-11 16:28:14 -07003246 // TODO: extend to support OBB mounts on secondary external storage
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003247
3248 // Only adjust paths when storage is emulated
3249 if (!Environment.isExternalStorageEmulated()) {
3250 return canonicalPath;
3251 }
3252
3253 String path = canonicalPath.toString();
3254
3255 // First trim off any external storage prefix
3256 final UserEnvironment userEnv = new UserEnvironment(userId);
3257
3258 // /storage/emulated/0
Jeff Sharkey1abdb712013-08-11 16:28:14 -07003259 final String externalPath = userEnv.getExternalStorageDirectory().getAbsolutePath();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003260 // /storage/emulated_legacy
3261 final String legacyExternalPath = Environment.getLegacyExternalStorageDirectory()
Jeff Sharkey1abdb712013-08-11 16:28:14 -07003262 .getAbsolutePath();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003263
3264 if (path.startsWith(externalPath)) {
3265 path = path.substring(externalPath.length() + 1);
3266 } else if (path.startsWith(legacyExternalPath)) {
3267 path = path.substring(legacyExternalPath.length() + 1);
3268 } else {
3269 return canonicalPath;
3270 }
3271
3272 // Handle special OBB paths on emulated storage
3273 final String obbPath = "Android/obb";
3274 if (path.startsWith(obbPath)) {
3275 path = path.substring(obbPath.length() + 1);
3276
Jeff Sharkey48877892015-03-18 11:27:19 -07003277 final UserEnvironment ownerEnv = new UserEnvironment(UserHandle.USER_OWNER);
3278 return new File(ownerEnv.buildExternalStorageAndroidObbDirs()[0], path)
3279 .getAbsolutePath();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003280 }
3281
3282 // Handle normal external storage paths
Jeff Sharkey48877892015-03-18 11:27:19 -07003283 return new File(userEnv.getExternalStorageDirectory(), path).getAbsolutePath();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003284 }
3285
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07003286 private static class Callbacks extends Handler {
3287 private static final int MSG_STORAGE_STATE_CHANGED = 1;
3288 private static final int MSG_VOLUME_STATE_CHANGED = 2;
Jeff Sharkey50a05452015-04-29 11:24:52 -07003289 private static final int MSG_VOLUME_RECORD_CHANGED = 3;
3290 private static final int MSG_VOLUME_FORGOTTEN = 4;
3291 private static final int MSG_DISK_SCANNED = 5;
Makoto Onuki9dc575d2015-06-12 16:10:25 -07003292 private static final int MSG_DISK_DESTROYED = 6;
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07003293
3294 private final RemoteCallbackList<IMountServiceListener>
3295 mCallbacks = new RemoteCallbackList<>();
3296
3297 public Callbacks(Looper looper) {
3298 super(looper);
3299 }
3300
3301 public void register(IMountServiceListener callback) {
3302 mCallbacks.register(callback);
3303 }
3304
3305 public void unregister(IMountServiceListener callback) {
3306 mCallbacks.unregister(callback);
3307 }
3308
3309 @Override
3310 public void handleMessage(Message msg) {
3311 final SomeArgs args = (SomeArgs) msg.obj;
3312 final int n = mCallbacks.beginBroadcast();
3313 for (int i = 0; i < n; i++) {
3314 final IMountServiceListener callback = mCallbacks.getBroadcastItem(i);
3315 try {
3316 invokeCallback(callback, msg.what, args);
3317 } catch (RemoteException ignored) {
3318 }
3319 }
3320 mCallbacks.finishBroadcast();
3321 args.recycle();
3322 }
3323
3324 private void invokeCallback(IMountServiceListener callback, int what, SomeArgs args)
3325 throws RemoteException {
3326 switch (what) {
3327 case MSG_STORAGE_STATE_CHANGED: {
3328 callback.onStorageStateChanged((String) args.arg1, (String) args.arg2,
3329 (String) args.arg3);
3330 break;
3331 }
3332 case MSG_VOLUME_STATE_CHANGED: {
3333 callback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3);
3334 break;
3335 }
Jeff Sharkey50a05452015-04-29 11:24:52 -07003336 case MSG_VOLUME_RECORD_CHANGED: {
3337 callback.onVolumeRecordChanged((VolumeRecord) args.arg1);
3338 break;
3339 }
3340 case MSG_VOLUME_FORGOTTEN: {
3341 callback.onVolumeForgotten((String) args.arg1);
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07003342 break;
3343 }
Jeff Sharkey620b32b2015-04-23 19:36:02 -07003344 case MSG_DISK_SCANNED: {
3345 callback.onDiskScanned((DiskInfo) args.arg1, args.argi2);
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07003346 break;
3347 }
Makoto Onuki9dc575d2015-06-12 16:10:25 -07003348 case MSG_DISK_DESTROYED: {
3349 callback.onDiskDestroyed((DiskInfo) args.arg1);
3350 break;
3351 }
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07003352 }
3353 }
3354
3355 private void notifyStorageStateChanged(String path, String oldState, String newState) {
3356 final SomeArgs args = SomeArgs.obtain();
3357 args.arg1 = path;
3358 args.arg2 = oldState;
3359 args.arg3 = newState;
3360 obtainMessage(MSG_STORAGE_STATE_CHANGED, args).sendToTarget();
3361 }
3362
3363 private void notifyVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
3364 final SomeArgs args = SomeArgs.obtain();
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07003365 args.arg1 = vol.clone();
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07003366 args.argi2 = oldState;
3367 args.argi3 = newState;
3368 obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget();
3369 }
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07003370
Jeff Sharkey50a05452015-04-29 11:24:52 -07003371 private void notifyVolumeRecordChanged(VolumeRecord rec) {
3372 final SomeArgs args = SomeArgs.obtain();
3373 args.arg1 = rec.clone();
3374 obtainMessage(MSG_VOLUME_RECORD_CHANGED, args).sendToTarget();
3375 }
3376
3377 private void notifyVolumeForgotten(String fsUuid) {
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07003378 final SomeArgs args = SomeArgs.obtain();
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07003379 args.arg1 = fsUuid;
Jeff Sharkey50a05452015-04-29 11:24:52 -07003380 obtainMessage(MSG_VOLUME_FORGOTTEN, args).sendToTarget();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07003381 }
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07003382
Jeff Sharkey620b32b2015-04-23 19:36:02 -07003383 private void notifyDiskScanned(DiskInfo disk, int volumeCount) {
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07003384 final SomeArgs args = SomeArgs.obtain();
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07003385 args.arg1 = disk.clone();
Jeff Sharkey620b32b2015-04-23 19:36:02 -07003386 args.argi2 = volumeCount;
3387 obtainMessage(MSG_DISK_SCANNED, args).sendToTarget();
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07003388 }
Makoto Onuki9dc575d2015-06-12 16:10:25 -07003389
3390 private void notifyDiskDestroyed(DiskInfo disk) {
3391 final SomeArgs args = SomeArgs.obtain();
3392 args.arg1 = disk.clone();
3393 obtainMessage(MSG_DISK_DESTROYED, args).sendToTarget();
3394 }
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07003395 }
3396
Kenny Root38cf8862010-09-26 14:18:51 -07003397 @Override
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003398 protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
3399 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
3400
3401 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ", 160);
Jeff Sharkey27de30d2015-04-18 16:20:27 -07003402 synchronized (mLock) {
3403 pw.println("Disks:");
3404 pw.increaseIndent();
3405 for (int i = 0; i < mDisks.size(); i++) {
3406 final DiskInfo disk = mDisks.valueAt(i);
3407 disk.dump(pw);
3408 }
3409 pw.decreaseIndent();
3410
3411 pw.println();
3412 pw.println("Volumes:");
3413 pw.increaseIndent();
3414 for (int i = 0; i < mVolumes.size(); i++) {
3415 final VolumeInfo vol = mVolumes.valueAt(i);
3416 if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.id)) continue;
3417 vol.dump(pw);
3418 }
3419 pw.decreaseIndent();
3420
3421 pw.println();
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07003422 pw.println("Records:");
Jeff Sharkey27de30d2015-04-18 16:20:27 -07003423 pw.increaseIndent();
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07003424 for (int i = 0; i < mRecords.size(); i++) {
3425 final VolumeRecord note = mRecords.valueAt(i);
3426 note.dump(pw);
Jeff Sharkey27de30d2015-04-18 16:20:27 -07003427 }
3428 pw.decreaseIndent();
Jeff Sharkey275e3e42015-04-24 16:10:32 -07003429
3430 pw.println();
3431 pw.println("Primary storage UUID: " + mPrimaryStorageUuid);
Jeff Sharkey4c099d02015-05-15 13:45:00 -07003432 pw.println("Force adoptable: " + mForceAdoptable);
Jeff Sharkey27de30d2015-04-18 16:20:27 -07003433 }
Kenny Root38cf8862010-09-26 14:18:51 -07003434
Kenny Root38cf8862010-09-26 14:18:51 -07003435 synchronized (mObbMounts) {
Jeff Sharkey27de30d2015-04-18 16:20:27 -07003436 pw.println();
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003437 pw.println("mObbMounts:");
3438 pw.increaseIndent();
3439 final Iterator<Entry<IBinder, List<ObbState>>> binders = mObbMounts.entrySet()
3440 .iterator();
Kenny Rootaf9d6672010-10-08 09:21:39 -07003441 while (binders.hasNext()) {
3442 Entry<IBinder, List<ObbState>> e = binders.next();
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003443 pw.println(e.getKey() + ":");
3444 pw.increaseIndent();
Kenny Rootaf9d6672010-10-08 09:21:39 -07003445 final List<ObbState> obbStates = e.getValue();
Kenny Root38cf8862010-09-26 14:18:51 -07003446 for (final ObbState obbState : obbStates) {
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003447 pw.println(obbState);
Kenny Root38cf8862010-09-26 14:18:51 -07003448 }
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003449 pw.decreaseIndent();
Kenny Root38cf8862010-09-26 14:18:51 -07003450 }
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003451 pw.decreaseIndent();
Kenny Rootaf9d6672010-10-08 09:21:39 -07003452
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003453 pw.println();
3454 pw.println("mObbPathToStateMap:");
3455 pw.increaseIndent();
Kenny Rootaf9d6672010-10-08 09:21:39 -07003456 final Iterator<Entry<String, ObbState>> maps = mObbPathToStateMap.entrySet().iterator();
3457 while (maps.hasNext()) {
3458 final Entry<String, ObbState> e = maps.next();
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003459 pw.print(e.getKey());
3460 pw.print(" -> ");
3461 pw.println(e.getValue());
Kenny Rootaf9d6672010-10-08 09:21:39 -07003462 }
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003463 pw.decreaseIndent();
Kenny Root38cf8862010-09-26 14:18:51 -07003464 }
Kenny Root4161f9b2011-07-13 09:48:33 -07003465
Robert Greenwalt470fd722012-01-18 12:51:15 -08003466 pw.println();
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003467 pw.println("mConnection:");
3468 pw.increaseIndent();
Robert Greenwalt470fd722012-01-18 12:51:15 -08003469 mConnector.dump(fd, pw, args);
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003470 pw.decreaseIndent();
Christopher Tate7265abe2014-11-21 13:54:45 -08003471
Christopher Tate7265abe2014-11-21 13:54:45 -08003472 pw.println();
3473 pw.print("Last maintenance: ");
Jeff Sharkeye8a4b662015-06-27 15:43:45 -07003474 pw.println(TimeUtils.formatForLogging(mLastMaintenance));
Kenny Root38cf8862010-09-26 14:18:51 -07003475 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003476
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -07003477 /** {@inheritDoc} */
Jeff Sharkey48877892015-03-18 11:27:19 -07003478 @Override
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -07003479 public void monitor() {
3480 if (mConnector != null) {
3481 mConnector.monitor();
3482 }
Paul Lawrence1c62cbb2015-06-03 14:14:52 -07003483 if (mCryptConnector != null) {
3484 mCryptConnector.monitor();
3485 }
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -07003486 }
Svet Ganov6ee871e2015-07-10 14:29:33 -07003487
3488 private final class MountServiceInternalImpl extends MountServiceInternal {
3489 // Not guarded by a lock.
3490 private final CopyOnWriteArrayList<ExternalStorageMountPolicy> mPolicies =
3491 new CopyOnWriteArrayList<>();
3492
3493 @Override
3494 public void addExternalStoragePolicy(ExternalStorageMountPolicy policy) {
3495 // No locking - CopyOnWriteArrayList
3496 mPolicies.add(policy);
3497 }
3498
3499 @Override
3500 public void onExternalStoragePolicyChanged(int uid, String packageName) {
3501 final int mountMode = getExternalStorageMountMode(uid, packageName);
3502 remountUidExternalStorage(uid, mountMode);
3503 }
3504
3505 @Override
3506 public int getExternalStorageMountMode(int uid, String packageName) {
3507 // No locking - CopyOnWriteArrayList
3508 int mountMode = Integer.MAX_VALUE;
3509 for (ExternalStorageMountPolicy policy : mPolicies) {
3510 final int policyMode = policy.getMountMode(uid, packageName);
3511 if (policyMode == Zygote.MOUNT_EXTERNAL_NONE) {
3512 return Zygote.MOUNT_EXTERNAL_NONE;
3513 }
3514 mountMode = Math.min(mountMode, policyMode);
3515 }
3516 if (mountMode == Integer.MAX_VALUE) {
3517 return Zygote.MOUNT_EXTERNAL_NONE;
3518 }
3519 return mountMode;
3520 }
3521
3522 public boolean hasExternalStorage(int uid, String packageName) {
Amith Yamasani2bd5cff2015-07-22 14:42:31 -07003523 // No need to check for system uid. This avoids a deadlock between
3524 // PackageManagerService and AppOpsService.
3525 if (uid == Process.SYSTEM_UID) {
3526 return true;
3527 }
Svet Ganov6ee871e2015-07-10 14:29:33 -07003528 // No locking - CopyOnWriteArrayList
3529 for (ExternalStorageMountPolicy policy : mPolicies) {
3530 final boolean policyHasStorage = policy.hasExternalStorage(uid, packageName);
3531 if (!policyHasStorage) {
3532 return false;
3533 }
3534 }
3535 return true;
3536 }
3537 }
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -07003538}