blob: 8430a0b2990604e0b48b2d923691d35a4ac6fbf3 [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;
Jeff Sharkey5217cac2015-12-20 15:34:01 -070027
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070028import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
29import static org.xmlpull.v1.XmlPullParser.START_TAG;
30
Jason parks8888c592011-01-20 22:46:41 -060031import android.Manifest;
Jeff Sharkeyef10ee02015-07-05 14:17:27 -070032import android.annotation.Nullable;
Xiaohui Chen621b3fc2015-10-02 14:41:42 -070033import android.app.ActivityManager;
Elliott Hughesf839b4f2014-09-26 12:30:47 -070034import android.app.ActivityManagerNative;
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -070035import android.app.AppOpsManager;
Jeff Sharkey14cbe522015-07-08 14:06:37 -070036import android.app.IActivityManager;
Jeff Sharkeybcd262d2015-06-10 09:41:17 -070037import android.content.BroadcastReceiver;
Kenny Roota02b8b02010-08-05 16:14:17 -070038import android.content.ComponentName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.content.Context;
40import android.content.Intent;
Jeff Sharkeybcd262d2015-06-10 09:41:17 -070041import android.content.IntentFilter;
Kenny Roota02b8b02010-08-05 16:14:17 -070042import android.content.ServiceConnection;
Jeff Sharkey275e3e42015-04-24 16:10:32 -070043import android.content.pm.IPackageMoveObserver;
44import android.content.pm.PackageManager;
Jeff Sharkey14cbe522015-07-08 14:06:37 -070045import android.content.pm.ProviderInfo;
Jeff Sharkeybcd262d2015-06-10 09:41:17 -070046import android.content.pm.UserInfo;
Elliott Hughesf839b4f2014-09-26 12:30:47 -070047import android.content.res.Configuration;
Kenny Root02c87302010-07-01 08:10:18 -070048import android.content.res.ObbInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import android.net.Uri;
Kenny Root02c87302010-07-01 08:10:18 -070050import android.os.Binder;
Jeff Sharkey4c099d02015-05-15 13:45:00 -070051import android.os.DropBoxManager;
Kenny Roota02b8b02010-08-05 16:14:17 -070052import android.os.Environment;
Jeff Sharkeyb049e212012-09-07 23:16:01 -070053import android.os.Environment.UserEnvironment;
Jeff Sharkey48877892015-03-18 11:27:19 -070054import android.os.FileUtils;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080055import android.os.Handler;
Dianne Hackbornefa92b22013-05-03 14:11:43 -070056import android.os.HandlerThread;
Kenny Roota02b8b02010-08-05 16:14:17 -070057import android.os.IBinder;
Daniel Sandler5f27ef42010-03-16 15:42:02 -040058import android.os.Looper;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080059import android.os.Message;
Daichi Hirono9e8d9e22015-11-13 14:37:00 +090060import android.os.ParcelFileDescriptor;
Jeff Sharkeyce14cd02015-12-07 15:35:42 -070061import android.os.PowerManager;
Jeff Sharkey9527b222015-06-24 15:24:48 -070062import android.os.Process;
Jeff Sharkey7151a9a2015-04-04 15:22:37 -070063import android.os.RemoteCallbackList;
San Mehat4270e1e2010-01-29 05:32:19 -080064import android.os.RemoteException;
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -080065import android.os.ServiceManager;
Jeff Sharkeyedcdaf62015-07-09 09:45:36 -070066import android.os.SystemClock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067import android.os.SystemProperties;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070068import android.os.UserHandle;
Emily Bernier92aa5a22014-07-07 10:11:48 -040069import android.os.UserManager;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070070import android.os.storage.DiskInfo;
Kenny Roota02b8b02010-08-05 16:14:17 -070071import android.os.storage.IMountService;
72import android.os.storage.IMountServiceListener;
73import android.os.storage.IMountShutdownObserver;
74import android.os.storage.IObbActionListener;
Svet Ganov6ee871e2015-07-10 14:29:33 -070075import android.os.storage.MountServiceInternal;
Kenny Rootaf9d6672010-10-08 09:21:39 -070076import android.os.storage.OnObbStateChangeListener;
Paul Lawrence46791e72014-04-03 09:10:26 -070077import android.os.storage.StorageManager;
Kenny Roota02b8b02010-08-05 16:14:17 -070078import android.os.storage.StorageResultCode;
Mike Lockwood2f6a3882011-05-09 19:08:06 -070079import android.os.storage.StorageVolume;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070080import android.os.storage.VolumeInfo;
Jeff Sharkeyb36586a2015-04-27 08:42:28 -070081import android.os.storage.VolumeRecord;
Jeff Sharkey14cbe522015-07-08 14:06:37 -070082import android.provider.MediaStore;
Jeff Sharkeye8a4b662015-06-27 15:43:45 -070083import android.provider.Settings;
Jason parksf7b3cd42011-01-27 09:28:25 -060084import android.text.TextUtils;
Jeff Sharkey1783f142015-04-17 10:52:51 -070085import android.text.format.DateUtils;
Jeff Sharkey48877892015-03-18 11:27:19 -070086import android.util.ArrayMap;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070087import android.util.AtomicFile;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070088import android.util.Log;
San Mehata5078592010-03-25 09:36:54 -070089import android.util.Slog;
Jeff Sharkeye8a4b662015-06-27 15:43:45 -070090import android.util.TimeUtils;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070091import android.util.Xml;
Jeff Sharkey48877892015-03-18 11:27:19 -070092
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -080093import com.android.internal.annotations.GuardedBy;
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 Sharkeyf9fc6d62015-11-08 16:46:05 -080099import com.android.internal.util.HexDump;
Jeff Sharkey5aca2b82013-10-16 16:21:54 -0700100import com.android.internal.util.IndentingPrintWriter;
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700101import com.android.internal.util.Preconditions;
Jeff Sharkeyb9fe5372015-12-03 15:23:08 -0700102import com.android.internal.widget.LockPatternUtils;
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700103import com.android.server.NativeDaemonConnector.Command;
Jeff Sharkey56cd6462013-06-07 15:09:15 -0700104import com.android.server.NativeDaemonConnector.SensitiveArg;
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700105import com.android.server.pm.PackageManagerService;
Kenny Roota02b8b02010-08-05 16:14:17 -0700106
Jeff Sharkey5217cac2015-12-20 15:34:01 -0700107import libcore.io.IoUtils;
108import libcore.util.EmptyArray;
109
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700110import org.xmlpull.v1.XmlPullParser;
111import org.xmlpull.v1.XmlPullParserException;
112import org.xmlpull.v1.XmlSerializer;
113
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700114import java.io.File;
Kenny Root38cf8862010-09-26 14:18:51 -0700115import java.io.FileDescriptor;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700116import java.io.FileInputStream;
117import java.io.FileNotFoundException;
Christopher Tate7265abe2014-11-21 13:54:45 -0800118import java.io.FileOutputStream;
Kenny Root05105f72010-09-22 17:29:43 -0700119import java.io.IOException;
Kenny Root38cf8862010-09-26 14:18:51 -0700120import java.io.PrintWriter;
Kenny Root3b1abba2010-10-13 15:00:07 -0700121import java.math.BigInteger;
Paul Lawrence8e397362014-01-27 15:22:30 -0800122import java.nio.charset.StandardCharsets;
Kenny Root735de3b2010-09-30 14:11:39 -0700123import java.security.NoSuchAlgorithmException;
Kenny Root3b1abba2010-10-13 15:00:07 -0700124import java.security.spec.InvalidKeySpecException;
125import java.security.spec.KeySpec;
San Mehat22dd86e2010-01-12 12:21:18 -0800126import java.util.ArrayList;
Jeff Sharkeyf9fc6d62015-11-08 16:46:05 -0800127import java.util.Arrays;
Kenny Roota02b8b02010-08-05 16:14:17 -0700128import java.util.HashMap;
San Mehat6cdd9c02010-02-09 14:45:20 -0800129import java.util.HashSet;
Kenny Root38cf8862010-09-26 14:18:51 -0700130import java.util.Iterator;
Kenny Roota02b8b02010-08-05 16:14:17 -0700131import java.util.LinkedList;
132import java.util.List;
Elliott Hughesf839b4f2014-09-26 12:30:47 -0700133import java.util.Locale;
Kenny Roota02b8b02010-08-05 16:14:17 -0700134import java.util.Map;
Kenny Root38cf8862010-09-26 14:18:51 -0700135import java.util.Map.Entry;
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700136import java.util.Objects;
Svet Ganov6ee871e2015-07-10 14:29:33 -0700137import java.util.concurrent.CopyOnWriteArrayList;
Kenny Root51a573c2012-05-17 13:30:28 -0700138import java.util.concurrent.CountDownLatch;
139import java.util.concurrent.TimeUnit;
Jeff Sharkeyedcdaf62015-07-09 09:45:36 -0700140import java.util.concurrent.TimeoutException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141
Kenny Root3b1abba2010-10-13 15:00:07 -0700142import javax.crypto.SecretKey;
143import javax.crypto.SecretKeyFactory;
144import javax.crypto.spec.PBEKeySpec;
145
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800146/**
Jeff Sharkey48877892015-03-18 11:27:19 -0700147 * Service responsible for various storage media. Connects to {@code vold} to
148 * watch for and manage dynamically added storage, such as SD cards and USB mass
149 * storage. Also decides how storage should be presented to users on the device.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800150 */
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -0700151class MountService extends IMountService.Stub
152 implements INativeDaemonConnectorCallbacks, Watchdog.Monitor {
Jason parks5af0b912010-11-29 09:05:25 -0600153
Christopher Tated417d622013-08-19 16:14:25 -0700154 // Static direct instance pointer for the tightly-coupled idle service to use
155 static MountService sSelf = null;
156
Jeff Sharkey56e62932015-03-21 20:41:00 -0700157 public static class Lifecycle extends SystemService {
158 private MountService mMountService;
159
160 public Lifecycle(Context context) {
161 super(context);
162 }
163
164 @Override
165 public void onStart() {
166 mMountService = new MountService(getContext());
167 publishBinderService("mount", mMountService);
Jeff Sharkeycd575992016-03-29 14:12:49 -0600168 mMountService.start();
Jeff Sharkey56e62932015-03-21 20:41:00 -0700169 }
170
171 @Override
172 public void onBootPhase(int phase) {
173 if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
174 mMountService.systemReady();
Yasuhiro Matsuda87a38b52015-07-24 22:10:16 +0900175 } else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
176 mMountService.bootCompleted();
Jeff Sharkey56e62932015-03-21 20:41:00 -0700177 }
178 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700179
180 @Override
Jeff Sharkeyab15c392016-05-05 11:45:01 -0600181 public void onSwitchUser(int userHandle) {
182 mMountService.mCurrentUserId = userHandle;
183 }
184
185 @Override
Jeff Sharkeyce14cd02015-12-07 15:35:42 -0700186 public void onUnlockUser(int userHandle) {
187 mMountService.onUnlockUser(userHandle);
Jeff Sharkey48877892015-03-18 11:27:19 -0700188 }
189
190 @Override
191 public void onCleanupUser(int userHandle) {
192 mMountService.onCleanupUser(userHandle);
193 }
Jeff Sharkey56e62932015-03-21 20:41:00 -0700194 }
195
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800196 private static final boolean DEBUG_EVENTS = false;
Kenny Rootb7db2722011-01-25 16:39:35 -0800197 private static final boolean DEBUG_OBB = false;
Kenny Root02c87302010-07-01 08:10:18 -0700198
Kenny Root07714d42011-08-17 17:49:28 -0700199 // Disable this since it messes up long-running cryptfs operations.
200 private static final boolean WATCHDOG_ENABLE = false;
201
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800202 private static final String TAG = "MountService";
Jeff Sharkeye8a4b662015-06-27 15:43:45 -0700203
Jeff Sharkey9756d752015-05-14 21:07:42 -0700204 private static final String TAG_STORAGE_BENCHMARK = "storage_benchmark";
Jeff Sharkeye8a4b662015-06-27 15:43:45 -0700205 private static final String TAG_STORAGE_TRIM = "storage_trim";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800206
Kenny Root305bcbf2010-09-03 07:56:38 -0700207 private static final String VOLD_TAG = "VoldConnector";
Paul Lawrence1c62cbb2015-06-03 14:14:52 -0700208 private static final String CRYPTD_TAG = "CryptdConnector";
Kenny Root305bcbf2010-09-03 07:56:38 -0700209
Kenny Rootcf0b38c2011-03-22 14:17:59 -0700210 /** Maximum number of ASEC containers allowed to be mounted. */
211 private static final int MAX_CONTAINERS = 250;
212
Jeff Sharkey275e3e42015-04-24 16:10:32 -0700213 /** Magic value sent by MoveTask.cpp */
214 private static final int MOVE_STATUS_COPY_FINISHED = 82;
215
San Mehat4270e1e2010-01-29 05:32:19 -0800216 /*
217 * Internal vold response code constants
218 */
San Mehat22dd86e2010-01-12 12:21:18 -0800219 class VoldResponseCode {
San Mehat4270e1e2010-01-29 05:32:19 -0800220 /*
221 * 100 series - Requestion action was initiated; expect another reply
222 * before proceeding with a new command.
223 */
San Mehat22dd86e2010-01-12 12:21:18 -0800224 public static final int VolumeListResult = 110;
225 public static final int AsecListResult = 111;
San Mehatc1b4ce92010-02-16 17:13:03 -0800226 public static final int StorageUsersListResult = 112;
Paul Lawrencee51dcf92014-03-18 10:56:00 -0700227 public static final int CryptfsGetfieldResult = 113;
San Mehat22dd86e2010-01-12 12:21:18 -0800228
San Mehat4270e1e2010-01-29 05:32:19 -0800229 /*
230 * 200 series - Requestion action has been successfully completed.
231 */
232 public static final int ShareStatusResult = 210;
San Mehat22dd86e2010-01-12 12:21:18 -0800233 public static final int AsecPathResult = 211;
San Mehat4270e1e2010-01-29 05:32:19 -0800234 public static final int ShareEnabledResult = 212;
San Mehat22dd86e2010-01-12 12:21:18 -0800235
San Mehat4270e1e2010-01-29 05:32:19 -0800236 /*
237 * 400 series - Command was accepted, but the requested action
238 * did not take place.
239 */
240 public static final int OpFailedNoMedia = 401;
241 public static final int OpFailedMediaBlank = 402;
242 public static final int OpFailedMediaCorrupt = 403;
243 public static final int OpFailedVolNotMounted = 404;
San Mehatd9709982010-02-18 11:43:03 -0800244 public static final int OpFailedStorageBusy = 405;
San Mehat2d66cef2010-03-23 11:12:52 -0700245 public static final int OpFailedStorageNotFound = 406;
San Mehat4270e1e2010-01-29 05:32:19 -0800246
247 /*
248 * 600 series - Unsolicited broadcasts.
249 */
Jeff Sharkey48877892015-03-18 11:27:19 -0700250 public static final int DISK_CREATED = 640;
251 public static final int DISK_SIZE_CHANGED = 641;
252 public static final int DISK_LABEL_CHANGED = 642;
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700253 public static final int DISK_SCANNED = 643;
Jeff Sharkeye8a4b662015-06-27 15:43:45 -0700254 public static final int DISK_SYS_PATH_CHANGED = 644;
Jeff Sharkey48877892015-03-18 11:27:19 -0700255 public static final int DISK_DESTROYED = 649;
256
257 public static final int VOLUME_CREATED = 650;
258 public static final int VOLUME_STATE_CHANGED = 651;
259 public static final int VOLUME_FS_TYPE_CHANGED = 652;
260 public static final int VOLUME_FS_UUID_CHANGED = 653;
261 public static final int VOLUME_FS_LABEL_CHANGED = 654;
262 public static final int VOLUME_PATH_CHANGED = 655;
Jeff Sharkey50a05452015-04-29 11:24:52 -0700263 public static final int VOLUME_INTERNAL_PATH_CHANGED = 656;
Jeff Sharkey48877892015-03-18 11:27:19 -0700264 public static final int VOLUME_DESTROYED = 659;
Svetoslavf23b64d2013-04-25 14:45:54 -0700265
Jeff Sharkey275e3e42015-04-24 16:10:32 -0700266 public static final int MOVE_STATUS = 660;
Jeff Sharkey9756d752015-05-14 21:07:42 -0700267 public static final int BENCHMARK_RESULT = 661;
Jeff Sharkeye8a4b662015-06-27 15:43:45 -0700268 public static final int TRIM_RESULT = 662;
San Mehat22dd86e2010-01-12 12:21:18 -0800269 }
270
Jeff Sharkey620b32b2015-04-23 19:36:02 -0700271 private static final int VERSION_INIT = 1;
272 private static final int VERSION_ADD_PRIMARY = 2;
Jeff Sharkeyfced5342015-05-10 14:53:34 -0700273 private static final int VERSION_FIX_PRIMARY = 3;
Jeff Sharkey620b32b2015-04-23 19:36:02 -0700274
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700275 private static final String TAG_VOLUMES = "volumes";
Jeff Sharkey620b32b2015-04-23 19:36:02 -0700276 private static final String ATTR_VERSION = "version";
277 private static final String ATTR_PRIMARY_STORAGE_UUID = "primaryStorageUuid";
Jeff Sharkey4c099d02015-05-15 13:45:00 -0700278 private static final String ATTR_FORCE_ADOPTABLE = "forceAdoptable";
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700279 private static final String TAG_VOLUME = "volume";
280 private static final String ATTR_TYPE = "type";
281 private static final String ATTR_FS_UUID = "fsUuid";
Jeff Sharkey5cc0df22015-06-17 19:44:05 -0700282 private static final String ATTR_PART_GUID = "partGuid";
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700283 private static final String ATTR_NICKNAME = "nickname";
284 private static final String ATTR_USER_FLAGS = "userFlags";
Jeff Sharkeye8a4b662015-06-27 15:43:45 -0700285 private static final String ATTR_CREATED_MILLIS = "createdMillis";
286 private static final String ATTR_LAST_TRIM_MILLIS = "lastTrimMillis";
287 private static final String ATTR_LAST_BENCH_MILLIS = "lastBenchMillis";
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700288
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700289 private final AtomicFile mSettingsFile;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700290
Jeff Sharkey48877892015-03-18 11:27:19 -0700291 /**
292 * <em>Never</em> hold the lock while performing downcalls into vold, since
293 * unsolicited events can suddenly appear to update data structures.
294 */
295 private final Object mLock = new Object();
296
Jeff Sharkeyce14cd02015-12-07 15:35:42 -0700297 /** Set of users that we know are unlocked. */
Jeff Sharkey48877892015-03-18 11:27:19 -0700298 @GuardedBy("mLock")
Jeff Sharkeyce14cd02015-12-07 15:35:42 -0700299 private int[] mLocalUnlockedUsers = EmptyArray.INT;
300 /** Set of users that system knows are unlocked. */
Jeff Sharkeyf9fc6d62015-11-08 16:46:05 -0800301 @GuardedBy("mLock")
Jeff Sharkeyce14cd02015-12-07 15:35:42 -0700302 private int[] mSystemUnlockedUsers = EmptyArray.INT;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700303
304 /** Map from disk ID to disk */
Jeff Sharkey48877892015-03-18 11:27:19 -0700305 @GuardedBy("mLock")
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700306 private ArrayMap<String, DiskInfo> mDisks = new ArrayMap<>();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700307 /** Map from volume ID to disk */
Jeff Sharkey48877892015-03-18 11:27:19 -0700308 @GuardedBy("mLock")
Xiaohui Chen621b3fc2015-10-02 14:41:42 -0700309 private final ArrayMap<String, VolumeInfo> mVolumes = new ArrayMap<>();
Jeff Sharkey48877892015-03-18 11:27:19 -0700310
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700311 /** Map from UUID to record */
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700312 @GuardedBy("mLock")
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700313 private ArrayMap<String, VolumeRecord> mRecords = new ArrayMap<>();
Jeff Sharkey620b32b2015-04-23 19:36:02 -0700314 @GuardedBy("mLock")
315 private String mPrimaryStorageUuid;
Jeff Sharkey4c099d02015-05-15 13:45:00 -0700316 @GuardedBy("mLock")
317 private boolean mForceAdoptable;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700318
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700319 /** Map from disk ID to latches */
320 @GuardedBy("mLock")
321 private ArrayMap<String, CountDownLatch> mDiskScanLatches = new ArrayMap<>();
322
Jeff Sharkey275e3e42015-04-24 16:10:32 -0700323 @GuardedBy("mLock")
324 private IPackageMoveObserver mMoveCallback;
325 @GuardedBy("mLock")
326 private String mMoveTargetUuid;
327
Jeff Sharkeyab15c392016-05-05 11:45:01 -0600328 private volatile int mCurrentUserId = UserHandle.USER_SYSTEM;
329
Jeff Sharkeyef10ee02015-07-05 14:17:27 -0700330 private VolumeInfo findVolumeByIdOrThrow(String id) {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700331 synchronized (mLock) {
332 final VolumeInfo vol = mVolumes.get(id);
333 if (vol != null) {
334 return vol;
335 }
336 }
337 throw new IllegalArgumentException("No volume found for ID " + id);
338 }
339
Jeff Sharkeyef10ee02015-07-05 14:17:27 -0700340 private String findVolumeIdForPathOrThrow(String path) {
Jeff Sharkey48877892015-03-18 11:27:19 -0700341 synchronized (mLock) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700342 for (int i = 0; i < mVolumes.size(); i++) {
343 final VolumeInfo vol = mVolumes.valueAt(i);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700344 if (vol.path != null && path.startsWith(vol.path)) {
345 return vol.id;
Jeff Sharkey48877892015-03-18 11:27:19 -0700346 }
347 }
348 }
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700349 throw new IllegalArgumentException("No volume found for path " + path);
Jeff Sharkey48877892015-03-18 11:27:19 -0700350 }
351
Jeff Sharkeye8a4b662015-06-27 15:43:45 -0700352 private VolumeRecord findRecordForPath(String path) {
353 synchronized (mLock) {
354 for (int i = 0; i < mVolumes.size(); i++) {
355 final VolumeInfo vol = mVolumes.valueAt(i);
356 if (vol.path != null && path.startsWith(vol.path)) {
357 return mRecords.get(vol.fsUuid);
358 }
359 }
360 }
361 return null;
362 }
363
364 private String scrubPath(String path) {
365 if (path.startsWith(Environment.getDataDirectory().getAbsolutePath())) {
366 return "internal";
367 }
368 final VolumeRecord rec = findRecordForPath(path);
369 if (rec == null || rec.createdMillis == 0) {
370 return "unknown";
371 } else {
372 return "ext:" + (int) ((System.currentTimeMillis() - rec.createdMillis)
373 / DateUtils.WEEK_IN_MILLIS) + "w";
374 }
375 }
376
Jeff Sharkeyef10ee02015-07-05 14:17:27 -0700377 private @Nullable VolumeInfo findStorageForUuid(String volumeUuid) {
Jeff Sharkey275e3e42015-04-24 16:10:32 -0700378 final StorageManager storage = mContext.getSystemService(StorageManager.class);
379 if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) {
Jeff Sharkeyef10ee02015-07-05 14:17:27 -0700380 return storage.findVolumeById(VolumeInfo.ID_EMULATED_INTERNAL);
Jeff Sharkey275e3e42015-04-24 16:10:32 -0700381 } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
382 return storage.getPrimaryPhysicalVolume();
383 } else {
384 return storage.findEmulatedForPrivate(storage.findVolumeByUuid(volumeUuid));
385 }
386 }
387
Jeff Sharkeye8a4b662015-06-27 15:43:45 -0700388 private boolean shouldBenchmark() {
389 final long benchInterval = Settings.Global.getLong(mContext.getContentResolver(),
390 Settings.Global.STORAGE_BENCHMARK_INTERVAL, DateUtils.WEEK_IN_MILLIS);
Jeff Sharkeye83d8a92015-09-09 14:53:38 -0700391 if (benchInterval == -1) {
392 return false;
393 } else if (benchInterval == 0) {
394 return true;
395 }
396
Jeff Sharkeye8a4b662015-06-27 15:43:45 -0700397 synchronized (mLock) {
398 for (int i = 0; i < mVolumes.size(); i++) {
399 final VolumeInfo vol = mVolumes.valueAt(i);
400 final VolumeRecord rec = mRecords.get(vol.fsUuid);
Jeff Sharkeye83d8a92015-09-09 14:53:38 -0700401 if (vol.isMountedWritable() && rec != null) {
Jeff Sharkeye8a4b662015-06-27 15:43:45 -0700402 final long benchAge = System.currentTimeMillis() - rec.lastBenchMillis;
403 if (benchAge >= benchInterval) {
404 return true;
405 }
406 }
407 }
408 return false;
409 }
410 }
411
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700412 private CountDownLatch findOrCreateDiskScanLatch(String diskId) {
413 synchronized (mLock) {
414 CountDownLatch latch = mDiskScanLatches.get(diskId);
415 if (latch == null) {
416 latch = new CountDownLatch(1);
417 mDiskScanLatches.put(diskId, latch);
418 }
419 return latch;
420 }
421 }
422
Jeff Sharkeyf9fc6d62015-11-08 16:46:05 -0800423 private static String escapeNull(String arg) {
424 if (TextUtils.isEmpty(arg)) {
425 return "!";
426 } else {
427 if (arg.indexOf('\0') != -1 || arg.indexOf(' ') != -1) {
428 throw new IllegalArgumentException(arg);
429 }
430 return arg;
431 }
432 }
433
Paul Lawrence8e397362014-01-27 15:22:30 -0800434 /** List of crypto types.
435 * These must match CRYPT_TYPE_XXX in cryptfs.h AND their
436 * corresponding commands in CommandListener.cpp */
437 public static final String[] CRYPTO_TYPES
438 = { "password", "default", "pattern", "pin" };
439
Brian Carlstrom7395a8a2014-04-28 22:11:01 -0700440 private final Context mContext;
Jeff Sharkeycd575992016-03-29 14:12:49 -0600441
Brian Carlstromdfad99a2014-05-07 15:21:14 -0700442 private final NativeDaemonConnector mConnector;
Paul Lawrence1c62cbb2015-06-03 14:14:52 -0700443 private final NativeDaemonConnector mCryptConnector;
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700444
Jeff Sharkeycd575992016-03-29 14:12:49 -0600445 private final Thread mConnectorThread;
446 private final Thread mCryptConnectorThread;
447
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700448 private volatile boolean mSystemReady = false;
Yasuhiro Matsuda87a38b52015-07-24 22:10:16 +0900449 private volatile boolean mBootCompleted = false;
Jeff Sharkey48877892015-03-18 11:27:19 -0700450 private volatile boolean mDaemonConnected = false;
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700451
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700452 private PackageManagerService mPms;
453
454 private final Callbacks mCallbacks;
Jeff Sharkeyb9fe5372015-12-03 15:23:08 -0700455 private final LockPatternUtils mLockPatternUtils;
Jeff Sharkey48877892015-03-18 11:27:19 -0700456
Paul Lawrence1c62cbb2015-06-03 14:14:52 -0700457 // Two connectors - mConnector & mCryptConnector
458 private final CountDownLatch mConnectedSignal = new CountDownLatch(2);
Jeff Sharkey0be607c2012-11-14 14:39:19 -0800459 private final CountDownLatch mAsecsScanned = new CountDownLatch(1);
Jeff Sharkey48877892015-03-18 11:27:19 -0700460
461 private final Object mUnmountLock = new Object();
462 @GuardedBy("mUnmountLock")
463 private CountDownLatch mUnmountSignal;
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -0800464
San Mehat6cdd9c02010-02-09 14:45:20 -0800465 /**
466 * Private hash of currently mounted secure containers.
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800467 * Used as a lock in methods to manipulate secure containers.
San Mehat6cdd9c02010-02-09 14:45:20 -0800468 */
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800469 final private HashSet<String> mAsecMountSet = new HashSet<String>();
San Mehat6cdd9c02010-02-09 14:45:20 -0800470
Kenny Root02c87302010-07-01 08:10:18 -0700471 /**
Kenny Root3b1abba2010-10-13 15:00:07 -0700472 * The size of the crypto algorithm key in bits for OBB files. Currently
473 * Twofish is used which takes 128-bit keys.
474 */
475 private static final int CRYPTO_ALGORITHM_KEY_SIZE = 128;
476
477 /**
478 * The number of times to run SHA1 in the PBKDF2 function for OBB files.
479 * 1024 is reasonably secure and not too slow.
480 */
481 private static final int PBKDF2_HASH_ROUNDS = 1024;
482
483 /**
Kenny Roota02b8b02010-08-05 16:14:17 -0700484 * Mounted OBB tracking information. Used to track the current state of all
485 * OBBs.
Kenny Root02c87302010-07-01 08:10:18 -0700486 */
Kenny Root735de3b2010-09-30 14:11:39 -0700487 final private Map<IBinder, List<ObbState>> mObbMounts = new HashMap<IBinder, List<ObbState>>();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700488
489 /** Map from raw paths to {@link ObbState}. */
Kenny Roota02b8b02010-08-05 16:14:17 -0700490 final private Map<String, ObbState> mObbPathToStateMap = new HashMap<String, ObbState>();
491
Svet Ganov6ee871e2015-07-10 14:29:33 -0700492 // Not guarded by a lock.
493 private final MountServiceInternalImpl mMountServiceInternal = new MountServiceInternalImpl();
494
Kenny Roota02b8b02010-08-05 16:14:17 -0700495 class ObbState implements IBinder.DeathRecipient {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700496 public ObbState(String rawPath, String canonicalPath, int callingUid,
497 IObbActionListener token, int nonce) {
498 this.rawPath = rawPath;
Xiaohui Chen621b3fc2015-10-02 14:41:42 -0700499 this.canonicalPath = canonicalPath;
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700500
501 this.ownerGid = UserHandle.getSharedAppGid(callingUid);
Kenny Rootaf9d6672010-10-08 09:21:39 -0700502 this.token = token;
503 this.nonce = nonce;
Kenny Roota02b8b02010-08-05 16:14:17 -0700504 }
505
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700506 final String rawPath;
507 final String canonicalPath;
Kenny Roota02b8b02010-08-05 16:14:17 -0700508
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700509 final int ownerGid;
Kenny Roota02b8b02010-08-05 16:14:17 -0700510
Kenny Rootaf9d6672010-10-08 09:21:39 -0700511 // Token of remote Binder caller
512 final IObbActionListener token;
513
514 // Identifier to pass back to the token
515 final int nonce;
Kenny Roota02b8b02010-08-05 16:14:17 -0700516
Kenny Root735de3b2010-09-30 14:11:39 -0700517 public IBinder getBinder() {
518 return token.asBinder();
519 }
520
Kenny Roota02b8b02010-08-05 16:14:17 -0700521 @Override
522 public void binderDied() {
523 ObbAction action = new UnmountObbAction(this, true);
524 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
Kenny Root735de3b2010-09-30 14:11:39 -0700525 }
Kenny Roota02b8b02010-08-05 16:14:17 -0700526
Kenny Root5919ac62010-10-05 09:49:40 -0700527 public void link() throws RemoteException {
528 getBinder().linkToDeath(this, 0);
529 }
530
531 public void unlink() {
Kenny Root735de3b2010-09-30 14:11:39 -0700532 getBinder().unlinkToDeath(this, 0);
Kenny Roota02b8b02010-08-05 16:14:17 -0700533 }
Kenny Root38cf8862010-09-26 14:18:51 -0700534
535 @Override
536 public String toString() {
537 StringBuilder sb = new StringBuilder("ObbState{");
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700538 sb.append("rawPath=").append(rawPath);
539 sb.append(",canonicalPath=").append(canonicalPath);
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700540 sb.append(",ownerGid=").append(ownerGid);
541 sb.append(",token=").append(token);
542 sb.append(",binder=").append(getBinder());
Kenny Root38cf8862010-09-26 14:18:51 -0700543 sb.append('}');
544 return sb.toString();
545 }
Kenny Roota02b8b02010-08-05 16:14:17 -0700546 }
547
548 // OBB Action Handler
549 final private ObbActionHandler mObbActionHandler;
550
551 // OBB action handler messages
552 private static final int OBB_RUN_ACTION = 1;
553 private static final int OBB_MCS_BOUND = 2;
554 private static final int OBB_MCS_UNBIND = 3;
555 private static final int OBB_MCS_RECONNECT = 4;
Kenny Rootaf9d6672010-10-08 09:21:39 -0700556 private static final int OBB_FLUSH_MOUNT_STATE = 5;
Kenny Roota02b8b02010-08-05 16:14:17 -0700557
558 /*
559 * Default Container Service information
560 */
561 static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
562 "com.android.defcontainer", "com.android.defcontainer.DefaultContainerService");
563
564 final private DefaultContainerConnection mDefContainerConn = new DefaultContainerConnection();
565
566 class DefaultContainerConnection implements ServiceConnection {
Jeff Sharkey48877892015-03-18 11:27:19 -0700567 @Override
Kenny Roota02b8b02010-08-05 16:14:17 -0700568 public void onServiceConnected(ComponentName name, IBinder service) {
569 if (DEBUG_OBB)
570 Slog.i(TAG, "onServiceConnected");
571 IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(service);
572 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_MCS_BOUND, imcs));
573 }
574
Jeff Sharkey48877892015-03-18 11:27:19 -0700575 @Override
Kenny Roota02b8b02010-08-05 16:14:17 -0700576 public void onServiceDisconnected(ComponentName name) {
577 if (DEBUG_OBB)
578 Slog.i(TAG, "onServiceDisconnected");
579 }
580 };
581
582 // Used in the ObbActionHandler
583 private IMediaContainerService mContainerService = null;
Kenny Root02c87302010-07-01 08:10:18 -0700584
Christopher Tate7265abe2014-11-21 13:54:45 -0800585 // Last fstrim operation tracking
586 private static final String LAST_FSTRIM_FILE = "last-fstrim";
587 private final File mLastMaintenanceFile;
588 private long mLastMaintenance;
589
Kenny Root02c87302010-07-01 08:10:18 -0700590 // Handler messages
Jeff Sharkey48877892015-03-18 11:27:19 -0700591 private static final int H_SYSTEM_READY = 1;
592 private static final int H_DAEMON_CONNECTED = 2;
593 private static final int H_SHUTDOWN = 3;
594 private static final int H_FSTRIM = 4;
595 private static final int H_VOLUME_MOUNT = 5;
596 private static final int H_VOLUME_BROADCAST = 6;
Jeff Sharkeyabc3e852015-08-03 14:41:13 -0700597 private static final int H_INTERNAL_BROADCAST = 7;
Xiaohui Chen621b3fc2015-10-02 14:41:42 -0700598 private static final int H_VOLUME_UNMOUNT = 8;
Jeff Sharkey5a9bb742015-11-03 10:15:57 -0800599 private static final int H_PARTITION_FORGET = 9;
600 private static final int H_RESET = 10;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800601
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400602 class MountServiceHandler extends Handler {
Jeff Sharkey48877892015-03-18 11:27:19 -0700603 public MountServiceHandler(Looper looper) {
604 super(looper);
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400605 }
606
Jason parks5af0b912010-11-29 09:05:25 -0600607 @Override
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800608 public void handleMessage(Message msg) {
609 switch (msg.what) {
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700610 case H_SYSTEM_READY: {
Jeff Sharkey48877892015-03-18 11:27:19 -0700611 handleSystemReady();
612 break;
613 }
614 case H_DAEMON_CONNECTED: {
615 handleDaemonConnected();
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700616 break;
617 }
Christopher Tated417d622013-08-19 16:14:25 -0700618 case H_FSTRIM: {
Jeff Sharkey1783f142015-04-17 10:52:51 -0700619 if (!isReady()) {
620 Slog.i(TAG, "fstrim requested, but no daemon connection yet; trying again");
Christopher Tate7618db12015-04-28 16:32:55 -0700621 sendMessageDelayed(obtainMessage(H_FSTRIM, msg.obj),
622 DateUtils.SECOND_IN_MILLIS);
623 break;
Jeff Sharkey1783f142015-04-17 10:52:51 -0700624 }
625
Christopher Tated417d622013-08-19 16:14:25 -0700626 Slog.i(TAG, "Running fstrim idle maintenance");
Christopher Tate7265abe2014-11-21 13:54:45 -0800627
628 // Remember when we kicked it off
629 try {
630 mLastMaintenance = System.currentTimeMillis();
631 mLastMaintenanceFile.setLastModified(mLastMaintenance);
632 } catch (Exception e) {
633 Slog.e(TAG, "Unable to record last fstrim!");
634 }
635
Jeff Sharkeye8a4b662015-06-27 15:43:45 -0700636 final boolean shouldBenchmark = shouldBenchmark();
Christopher Tated417d622013-08-19 16:14:25 -0700637 try {
638 // This method must be run on the main (handler) thread,
639 // so it is safe to directly call into vold.
Jeff Sharkeye8a4b662015-06-27 15:43:45 -0700640 mConnector.execute("fstrim", shouldBenchmark ? "dotrimbench" : "dotrim");
Christopher Tated417d622013-08-19 16:14:25 -0700641 } catch (NativeDaemonConnectorException ndce) {
642 Slog.e(TAG, "Failed to run fstrim!");
643 }
Christopher Tate7265abe2014-11-21 13:54:45 -0800644
Christopher Tated417d622013-08-19 16:14:25 -0700645 // invoke the completion callback, if any
Jeff Sharkeye8a4b662015-06-27 15:43:45 -0700646 // TODO: fstrim is non-blocking, so remove this useless callback
Christopher Tated417d622013-08-19 16:14:25 -0700647 Runnable callback = (Runnable) msg.obj;
648 if (callback != null) {
649 callback.run();
650 }
651 break;
652 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700653 case H_SHUTDOWN: {
654 final IMountShutdownObserver obs = (IMountShutdownObserver) msg.obj;
655 boolean success = false;
656 try {
657 success = mConnector.execute("volume", "shutdown").isClassOk();
658 } catch (NativeDaemonConnectorException ignored) {
659 }
660 if (obs != null) {
661 try {
662 obs.onShutDownComplete(success ? 0 : -1);
663 } catch (RemoteException ignored) {
664 }
665 }
666 break;
667 }
668 case H_VOLUME_MOUNT: {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700669 final VolumeInfo vol = (VolumeInfo) msg.obj;
Jeff Sharkey2e606d72015-07-27 14:19:54 -0700670 if (isMountDisallowed(vol)) {
671 Slog.i(TAG, "Ignoring mount " + vol.getId() + " due to policy");
672 break;
673 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700674 try {
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700675 mConnector.execute("volume", "mount", vol.id, vol.mountFlags,
676 vol.mountUserId);
Jeff Sharkey48877892015-03-18 11:27:19 -0700677 } catch (NativeDaemonConnectorException ignored) {
678 }
679 break;
680 }
Xiaohui Chen621b3fc2015-10-02 14:41:42 -0700681 case H_VOLUME_UNMOUNT: {
682 final VolumeInfo vol = (VolumeInfo) msg.obj;
683 unmount(vol.getId());
684 break;
685 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700686 case H_VOLUME_BROADCAST: {
687 final StorageVolume userVol = (StorageVolume) msg.obj;
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700688 final String envState = userVol.getState();
689 Slog.d(TAG, "Volume " + userVol.getId() + " broadcasting " + envState + " to "
Jeff Sharkey48877892015-03-18 11:27:19 -0700690 + userVol.getOwner());
691
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700692 final String action = VolumeInfo.getBroadcastForEnvironment(envState);
Jeff Sharkey48877892015-03-18 11:27:19 -0700693 if (action != null) {
694 final Intent intent = new Intent(action,
695 Uri.fromFile(userVol.getPathFile()));
696 intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, userVol);
697 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
698 mContext.sendBroadcastAsUser(intent, userVol.getOwner());
699 }
700 break;
701 }
Jeff Sharkeyabc3e852015-08-03 14:41:13 -0700702 case H_INTERNAL_BROADCAST: {
703 // Internal broadcasts aimed at system components, not for
704 // third-party apps.
705 final Intent intent = (Intent) msg.obj;
706 mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
707 android.Manifest.permission.WRITE_MEDIA_STORAGE);
Jeff Sharkey5a9bb742015-11-03 10:15:57 -0800708 break;
709 }
710 case H_PARTITION_FORGET: {
711 final String partGuid = (String) msg.obj;
712 forgetPartition(partGuid);
713 break;
714 }
715 case H_RESET: {
716 resetIfReadyAndConnected();
717 break;
Jeff Sharkeyabc3e852015-08-03 14:41:13 -0700718 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800719 }
720 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700721 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700722
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700723 private final Handler mHandler;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800724
Jeff Sharkeybcd262d2015-06-10 09:41:17 -0700725 private BroadcastReceiver mUserReceiver = new BroadcastReceiver() {
726 @Override
727 public void onReceive(Context context, Intent intent) {
728 final String action = intent.getAction();
729 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
Xiaohui Chen621b3fc2015-10-02 14:41:42 -0700730 Preconditions.checkArgument(userId >= 0);
Jeff Sharkeybcd262d2015-06-10 09:41:17 -0700731
732 try {
733 if (Intent.ACTION_USER_ADDED.equals(action)) {
734 final UserManager um = mContext.getSystemService(UserManager.class);
735 final int userSerialNumber = um.getUserSerialNumber(userId);
736 mConnector.execute("volume", "user_added", userId, userSerialNumber);
737 } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
Xiaohui Chen621b3fc2015-10-02 14:41:42 -0700738 synchronized (mVolumes) {
739 final int size = mVolumes.size();
740 for (int i = 0; i < size; i++) {
741 final VolumeInfo vol = mVolumes.valueAt(i);
742 if (vol.mountUserId == userId) {
743 vol.mountUserId = UserHandle.USER_NULL;
744 mHandler.obtainMessage(H_VOLUME_UNMOUNT, vol).sendToTarget();
745 }
746 }
747 }
Jeff Sharkeybcd262d2015-06-10 09:41:17 -0700748 mConnector.execute("volume", "user_removed", userId);
749 }
750 } catch (NativeDaemonConnectorException e) {
751 Slog.w(TAG, "Failed to send user details to vold", e);
752 }
753 }
754 };
755
Jeff Sharkey56e62932015-03-21 20:41:00 -0700756 @Override
757 public void waitForAsecScan() {
Jeff Sharkey48877892015-03-18 11:27:19 -0700758 waitForLatch(mAsecsScanned, "mAsecsScanned");
Kenny Root51a573c2012-05-17 13:30:28 -0700759 }
760
San Mehat207e5382010-02-04 20:46:54 -0800761 private void waitForReady() {
Jeff Sharkey48877892015-03-18 11:27:19 -0700762 waitForLatch(mConnectedSignal, "mConnectedSignal");
Kenny Root51a573c2012-05-17 13:30:28 -0700763 }
764
Jeff Sharkey48877892015-03-18 11:27:19 -0700765 private void waitForLatch(CountDownLatch latch, String condition) {
Jeff Sharkeyedcdaf62015-07-09 09:45:36 -0700766 try {
767 waitForLatch(latch, condition, -1);
768 } catch (TimeoutException ignored) {
769 }
770 }
771
772 private void waitForLatch(CountDownLatch latch, String condition, long timeoutMillis)
773 throws TimeoutException {
774 final long startMillis = SystemClock.elapsedRealtime();
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700775 while (true) {
Kenny Root51a573c2012-05-17 13:30:28 -0700776 try {
777 if (latch.await(5000, TimeUnit.MILLISECONDS)) {
San Mehat207e5382010-02-04 20:46:54 -0800778 return;
Kenny Root51a573c2012-05-17 13:30:28 -0700779 } else {
780 Slog.w(TAG, "Thread " + Thread.currentThread().getName()
Jeff Sharkey48877892015-03-18 11:27:19 -0700781 + " still waiting for " + condition + "...");
San Mehat207e5382010-02-04 20:46:54 -0800782 }
Kenny Root51a573c2012-05-17 13:30:28 -0700783 } catch (InterruptedException e) {
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700784 Slog.w(TAG, "Interrupt while waiting for " + condition);
San Mehat207e5382010-02-04 20:46:54 -0800785 }
Jeff Sharkeyedcdaf62015-07-09 09:45:36 -0700786 if (timeoutMillis > 0 && SystemClock.elapsedRealtime() > startMillis + timeoutMillis) {
787 throw new TimeoutException("Thread " + Thread.currentThread().getName()
788 + " gave up waiting for " + condition + " after " + timeoutMillis + "ms");
789 }
San Mehat207e5382010-02-04 20:46:54 -0800790 }
San Mehat1f6301e2010-01-07 22:40:27 -0800791 }
Kenny Root02c87302010-07-01 08:10:18 -0700792
Paul Lawrence945490c2014-03-27 16:37:28 +0000793 private boolean isReady() {
794 try {
795 return mConnectedSignal.await(0, TimeUnit.MILLISECONDS);
796 } catch (InterruptedException e) {
797 return false;
798 }
799 }
800
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700801 private void handleSystemReady() {
Jeff Sharkey8924e872015-11-30 12:52:10 -0700802 initIfReadyAndConnected();
Jeff Sharkey5a9bb742015-11-03 10:15:57 -0800803 resetIfReadyAndConnected();
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700804
Jeff Sharkey48877892015-03-18 11:27:19 -0700805 // Start scheduling nominally-daily fstrim operations
Christopher Tate115afda2014-06-06 19:06:26 -0700806 MountServiceIdler.scheduleIdlePass(mContext);
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700807 }
808
Jeff Sharkey14cbe522015-07-08 14:06:37 -0700809 /**
810 * MediaProvider has a ton of code that makes assumptions about storage
811 * paths never changing, so we outright kill them to pick up new state.
812 */
813 @Deprecated
Xiaohui Chen621b3fc2015-10-02 14:41:42 -0700814 private void killMediaProvider(List<UserInfo> users) {
815 if (users == null) return;
816
Jeff Sharkeyb3cf9532015-07-17 15:12:39 -0700817 final long token = Binder.clearCallingIdentity();
818 try {
Xiaohui Chen621b3fc2015-10-02 14:41:42 -0700819 for (UserInfo user : users) {
820 // System user does not have media provider, so skip.
821 if (user.isSystemOnly()) continue;
822
Jeff Sharkey2a9e3f82015-12-18 10:57:58 -0700823 final ProviderInfo provider = mPms.resolveContentProvider(MediaStore.AUTHORITY,
Jeff Sharkey8a372a02016-03-16 16:25:45 -0600824 PackageManager.MATCH_DIRECT_BOOT_AWARE
825 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
826 user.id);
Xiaohui Chen621b3fc2015-10-02 14:41:42 -0700827 if (provider != null) {
828 final IActivityManager am = ActivityManagerNative.getDefault();
829 try {
Jeff Sharkey85f449e2016-06-23 09:26:00 -0600830 am.killApplication(provider.applicationInfo.packageName,
831 UserHandle.getAppId(provider.applicationInfo.uid),
832 UserHandle.USER_ALL, "vold reset");
Xiaohui Chen621b3fc2015-10-02 14:41:42 -0700833 // We only need to run this once. It will kill all users' media processes.
834 break;
835 } catch (RemoteException e) {
836 }
Jeff Sharkeyb3cf9532015-07-17 15:12:39 -0700837 }
Jeff Sharkey14cbe522015-07-08 14:06:37 -0700838 }
Jeff Sharkeyb3cf9532015-07-17 15:12:39 -0700839 } finally {
840 Binder.restoreCallingIdentity(token);
Jeff Sharkey14cbe522015-07-08 14:06:37 -0700841 }
842 }
843
Jeff Sharkey5a9bb742015-11-03 10:15:57 -0800844 private void addInternalVolumeLocked() {
Amith Yamasania7892482015-08-07 11:09:05 -0700845 // Create a stub volume that represents internal storage
846 final VolumeInfo internal = new VolumeInfo(VolumeInfo.ID_PRIVATE_INTERNAL,
847 VolumeInfo.TYPE_PRIVATE, null, null);
848 internal.state = VolumeInfo.STATE_MOUNTED;
849 internal.path = Environment.getDataDirectory().getAbsolutePath();
850 mVolumes.put(internal.id, internal);
851 }
852
Jeff Sharkey8924e872015-11-30 12:52:10 -0700853 private void initIfReadyAndConnected() {
854 Slog.d(TAG, "Thinking about init, mSystemReady=" + mSystemReady
855 + ", mDaemonConnected=" + mDaemonConnected);
Jeff Sharkeyce14cd02015-12-07 15:35:42 -0700856 if (mSystemReady && mDaemonConnected
Paul Lawrence20be5d62016-02-26 13:51:17 -0800857 && !StorageManager.isFileEncryptedNativeOnly()) {
Jeff Sharkeyce14cd02015-12-07 15:35:42 -0700858 // When booting a device without native support, make sure that our
859 // user directories are locked or unlocked based on the current
860 // emulation status.
Paul Lawrence20be5d62016-02-26 13:51:17 -0800861 final boolean initLocked = StorageManager.isFileEncryptedEmulatedOnly();
Paul Crowleyd94ab732016-02-15 06:44:51 +0000862 Slog.d(TAG, "Setting up emulation state, initlocked=" + initLocked);
Jeff Sharkeyce14cd02015-12-07 15:35:42 -0700863 final List<UserInfo> users = mContext.getSystemService(UserManager.class).getUsers();
Jeff Sharkey8924e872015-11-30 12:52:10 -0700864 for (UserInfo user : users) {
865 try {
Jeff Sharkeyce14cd02015-12-07 15:35:42 -0700866 if (initLocked) {
867 mCryptConnector.execute("cryptfs", "lock_user_key", user.id);
868 } else {
869 mCryptConnector.execute("cryptfs", "unlock_user_key", user.id,
Paul Crowleyd94ab732016-02-15 06:44:51 +0000870 user.serialNumber, "!", "!");
Jeff Sharkeyce14cd02015-12-07 15:35:42 -0700871 }
Jeff Sharkey8924e872015-11-30 12:52:10 -0700872 } catch (NativeDaemonConnectorException e) {
873 Slog.w(TAG, "Failed to init vold", e);
874 }
875 }
876 }
877 }
878
Jeff Sharkey5a9bb742015-11-03 10:15:57 -0800879 private void resetIfReadyAndConnected() {
Jeff Sharkey48877892015-03-18 11:27:19 -0700880 Slog.d(TAG, "Thinking about reset, mSystemReady=" + mSystemReady
881 + ", mDaemonConnected=" + mDaemonConnected);
882 if (mSystemReady && mDaemonConnected) {
Jeff Sharkey5a9bb742015-11-03 10:15:57 -0800883 final List<UserInfo> users = mContext.getSystemService(UserManager.class).getUsers();
Xiaohui Chen621b3fc2015-10-02 14:41:42 -0700884 killMediaProvider(users);
Jeff Sharkey14cbe522015-07-08 14:06:37 -0700885
Jeff Sharkeyce14cd02015-12-07 15:35:42 -0700886 final int[] systemUnlockedUsers;
Jeff Sharkey5a9bb742015-11-03 10:15:57 -0800887 synchronized (mLock) {
Jeff Sharkeyce14cd02015-12-07 15:35:42 -0700888 systemUnlockedUsers = mSystemUnlockedUsers;
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700889
Jeff Sharkey5a9bb742015-11-03 10:15:57 -0800890 mDisks.clear();
891 mVolumes.clear();
892
893 addInternalVolumeLocked();
894 }
Jeff Sharkey59d577a2015-04-11 21:27:21 -0700895
Jeff Sharkey48877892015-03-18 11:27:19 -0700896 try {
897 mConnector.execute("volume", "reset");
Jeff Sharkeybcd262d2015-06-10 09:41:17 -0700898
899 // Tell vold about all existing and started users
Jeff Sharkeybcd262d2015-06-10 09:41:17 -0700900 for (UserInfo user : users) {
901 mConnector.execute("volume", "user_added", user.id, user.serialNumber);
902 }
Jeff Sharkeyce14cd02015-12-07 15:35:42 -0700903 for (int userId : systemUnlockedUsers) {
Jeff Sharkeybcd262d2015-06-10 09:41:17 -0700904 mConnector.execute("volume", "user_started", userId);
Jeff Sharkey50a05452015-04-29 11:24:52 -0700905 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700906 } catch (NativeDaemonConnectorException e) {
907 Slog.w(TAG, "Failed to reset vold", e);
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700908 }
909 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700910 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700911
Jeff Sharkeyce14cd02015-12-07 15:35:42 -0700912 private void onUnlockUser(int userId) {
913 Slog.d(TAG, "onUnlockUser " + userId);
Jeff Sharkey48877892015-03-18 11:27:19 -0700914
915 // We purposefully block here to make sure that user-specific
916 // staging area is ready so it's ready for zygote-forked apps to
917 // bind mount against.
918 try {
Jeff Sharkeybcd262d2015-06-10 09:41:17 -0700919 mConnector.execute("volume", "user_started", userId);
Jeff Sharkey48877892015-03-18 11:27:19 -0700920 } catch (NativeDaemonConnectorException ignored) {
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700921 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700922
923 // Record user as started so newly mounted volumes kick off events
924 // correctly, then synthesize events for any already-mounted volumes.
925 synchronized (mVolumes) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700926 for (int i = 0; i < mVolumes.size(); i++) {
927 final VolumeInfo vol = mVolumes.valueAt(i);
Jeff Sharkey46349872015-07-28 10:49:47 -0700928 if (vol.isVisibleForRead(userId) && vol.isMountedReadable()) {
Svet Ganov6ee871e2015-07-10 14:29:33 -0700929 final StorageVolume userVol = vol.buildStorageVolume(mContext, userId, false);
Jeff Sharkey48877892015-03-18 11:27:19 -0700930 mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget();
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700931
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700932 final String envState = VolumeInfo.getEnvironmentForState(vol.getState());
933 mCallbacks.notifyStorageStateChanged(userVol.getPath(), envState, envState);
Jeff Sharkey48877892015-03-18 11:27:19 -0700934 }
935 }
Jeff Sharkeyce14cd02015-12-07 15:35:42 -0700936 mSystemUnlockedUsers = ArrayUtils.appendInt(mSystemUnlockedUsers, userId);
Jeff Sharkey48877892015-03-18 11:27:19 -0700937 }
938 }
939
940 private void onCleanupUser(int userId) {
941 Slog.d(TAG, "onCleanupUser " + userId);
942
943 try {
Jeff Sharkeybcd262d2015-06-10 09:41:17 -0700944 mConnector.execute("volume", "user_stopped", userId);
Jeff Sharkey48877892015-03-18 11:27:19 -0700945 } catch (NativeDaemonConnectorException ignored) {
946 }
947
948 synchronized (mVolumes) {
Jeff Sharkeyce14cd02015-12-07 15:35:42 -0700949 mSystemUnlockedUsers = ArrayUtils.removeInt(mSystemUnlockedUsers, userId);
Jeff Sharkey48877892015-03-18 11:27:19 -0700950 }
951 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700952
Christopher Tated417d622013-08-19 16:14:25 -0700953 void runIdleMaintenance(Runnable callback) {
954 mHandler.sendMessage(mHandler.obtainMessage(H_FSTRIM, callback));
955 }
956
Christopher Tate7265abe2014-11-21 13:54:45 -0800957 // Binder entry point for kicking off an immediate fstrim
958 @Override
959 public void runMaintenance() {
Jeff Sharkey48877892015-03-18 11:27:19 -0700960 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
Christopher Tate7265abe2014-11-21 13:54:45 -0800961 runIdleMaintenance(null);
962 }
963
964 @Override
965 public long lastMaintenance() {
966 return mLastMaintenance;
967 }
968
San Mehat4270e1e2010-01-29 05:32:19 -0800969 /**
San Mehat4270e1e2010-01-29 05:32:19 -0800970 * Callback from NativeDaemonConnector
971 */
Jeff Sharkey48877892015-03-18 11:27:19 -0700972 @Override
San Mehat4270e1e2010-01-29 05:32:19 -0800973 public void onDaemonConnected() {
Jeff Sharkey48877892015-03-18 11:27:19 -0700974 mDaemonConnected = true;
975 mHandler.obtainMessage(H_DAEMON_CONNECTED).sendToTarget();
976 }
977
978 private void handleDaemonConnected() {
Jeff Sharkey8924e872015-11-30 12:52:10 -0700979 initIfReadyAndConnected();
Jeff Sharkey5a9bb742015-11-03 10:15:57 -0800980 resetIfReadyAndConnected();
Jeff Sharkey48877892015-03-18 11:27:19 -0700981
San Mehat4270e1e2010-01-29 05:32:19 -0800982 /*
Jeff Sharkey48877892015-03-18 11:27:19 -0700983 * Now that we've done our initialization, release
984 * the hounds!
San Mehat4270e1e2010-01-29 05:32:19 -0800985 */
Jeff Sharkey48877892015-03-18 11:27:19 -0700986 mConnectedSignal.countDown();
Paul Lawrence1c62cbb2015-06-03 14:14:52 -0700987 if (mConnectedSignal.getCount() != 0) {
988 // More daemons need to connect
989 return;
990 }
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400991
Jeff Sharkey48877892015-03-18 11:27:19 -0700992 // On an encrypted device we can't see system properties yet, so pull
993 // the system locale out of the mount service.
994 if ("".equals(SystemProperties.get("vold.encrypt_progress"))) {
995 copyLocaleFromMountService();
996 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700997
Jeff Sharkey48877892015-03-18 11:27:19 -0700998 // Let package manager load internal ASECs.
999 mPms.scanAvailableAsecs();
Mike Lockwood7fa24aa2011-03-23 14:52:34 -04001000
Jeff Sharkey48877892015-03-18 11:27:19 -07001001 // Notify people waiting for ASECs to be scanned that it's done.
1002 mAsecsScanned.countDown();
San Mehat4270e1e2010-01-29 05:32:19 -08001003 }
1004
Elliott Hughesf839b4f2014-09-26 12:30:47 -07001005 private void copyLocaleFromMountService() {
1006 String systemLocale;
1007 try {
1008 systemLocale = getField(StorageManager.SYSTEM_LOCALE_KEY);
1009 } catch (RemoteException e) {
1010 return;
1011 }
1012 if (TextUtils.isEmpty(systemLocale)) {
1013 return;
1014 }
1015
1016 Slog.d(TAG, "Got locale " + systemLocale + " from mount service");
1017 Locale locale = Locale.forLanguageTag(systemLocale);
1018 Configuration config = new Configuration();
1019 config.setLocale(locale);
1020 try {
Seigo Nonaka4963dfe2016-03-31 20:50:21 +09001021 ActivityManagerNative.getDefault().updatePersistentConfiguration(config);
Elliott Hughesf839b4f2014-09-26 12:30:47 -07001022 } catch (RemoteException e) {
1023 Slog.e(TAG, "Error setting system locale from mount service", e);
1024 }
Elliott Hughes9c33f282014-10-13 12:39:56 -07001025
1026 // Temporary workaround for http://b/17945169.
1027 Slog.d(TAG, "Setting system properties to " + systemLocale + " from mount service");
Narayan Kamathd30dbb82015-01-15 14:48:15 +00001028 SystemProperties.set("persist.sys.locale", locale.toLanguageTag());
Elliott Hughesf839b4f2014-09-26 12:30:47 -07001029 }
1030
San Mehat4270e1e2010-01-29 05:32:19 -08001031 /**
San Mehat4270e1e2010-01-29 05:32:19 -08001032 * Callback from NativeDaemonConnector
1033 */
Jeff Sharkey48877892015-03-18 11:27:19 -07001034 @Override
Dianne Hackborn77b987f2014-02-26 16:20:52 -08001035 public boolean onCheckHoldWakeLock(int code) {
1036 return false;
1037 }
1038
1039 /**
1040 * Callback from NativeDaemonConnector
1041 */
Jeff Sharkey48877892015-03-18 11:27:19 -07001042 @Override
San Mehat4270e1e2010-01-29 05:32:19 -08001043 public boolean onEvent(int code, String raw, String[] cooked) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001044 synchronized (mLock) {
1045 return onEventLocked(code, raw, cooked);
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -08001046 }
Jeff Sharkey48877892015-03-18 11:27:19 -07001047 }
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07001048
Jeff Sharkey48877892015-03-18 11:27:19 -07001049 private boolean onEventLocked(int code, String raw, String[] cooked) {
1050 switch (code) {
1051 case VoldResponseCode.DISK_CREATED: {
1052 if (cooked.length != 3) break;
1053 final String id = cooked[1];
Jeff Sharkey74acbbb2015-04-21 12:14:03 -07001054 int flags = Integer.parseInt(cooked[2]);
Jeff Sharkey4c099d02015-05-15 13:45:00 -07001055 if (SystemProperties.getBoolean(StorageManager.PROP_FORCE_ADOPTABLE, false)
1056 || mForceAdoptable) {
Jeff Sharkey74acbbb2015-04-21 12:14:03 -07001057 flags |= DiskInfo.FLAG_ADOPTABLE;
1058 }
Jeff Sharkey6ed74182016-08-23 13:53:53 -06001059 // Adoptable storage isn't currently supported on FBE devices
1060 if (StorageManager.isFileEncryptedNativeOnly()) {
1061 flags &= ~DiskInfo.FLAG_ADOPTABLE;
1062 }
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001063 mDisks.put(id, new DiskInfo(id, flags));
Jeff Sharkey48877892015-03-18 11:27:19 -07001064 break;
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07001065 }
Jeff Sharkey48877892015-03-18 11:27:19 -07001066 case VoldResponseCode.DISK_SIZE_CHANGED: {
1067 if (cooked.length != 3) break;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001068 final DiskInfo disk = mDisks.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -07001069 if (disk != null) {
1070 disk.size = Long.parseLong(cooked[2]);
San Mehat4270e1e2010-01-29 05:32:19 -08001071 }
Jeff Sharkey48877892015-03-18 11:27:19 -07001072 break;
1073 }
1074 case VoldResponseCode.DISK_LABEL_CHANGED: {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001075 final DiskInfo disk = mDisks.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -07001076 if (disk != null) {
Jeff Sharkey27de30d2015-04-18 16:20:27 -07001077 final StringBuilder builder = new StringBuilder();
1078 for (int i = 2; i < cooked.length; i++) {
1079 builder.append(cooked[i]).append(' ');
1080 }
1081 disk.label = builder.toString().trim();
Jeff Sharkey48877892015-03-18 11:27:19 -07001082 }
1083 break;
1084 }
Jeff Sharkeyeba260d2015-04-19 14:35:16 -07001085 case VoldResponseCode.DISK_SCANNED: {
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07001086 if (cooked.length != 2) break;
1087 final DiskInfo disk = mDisks.get(cooked[1]);
Jeff Sharkeyeba260d2015-04-19 14:35:16 -07001088 if (disk != null) {
1089 onDiskScannedLocked(disk);
1090 }
Jeff Sharkey59d577a2015-04-11 21:27:21 -07001091 break;
1092 }
Jeff Sharkeye8a4b662015-06-27 15:43:45 -07001093 case VoldResponseCode.DISK_SYS_PATH_CHANGED: {
1094 if (cooked.length != 3) break;
1095 final DiskInfo disk = mDisks.get(cooked[1]);
1096 if (disk != null) {
1097 disk.sysPath = cooked[2];
1098 }
1099 break;
1100 }
Jeff Sharkey48877892015-03-18 11:27:19 -07001101 case VoldResponseCode.DISK_DESTROYED: {
1102 if (cooked.length != 2) break;
Makoto Onuki9dc575d2015-06-12 16:10:25 -07001103 final DiskInfo disk = mDisks.remove(cooked[1]);
1104 if (disk != null) {
1105 mCallbacks.notifyDiskDestroyed(disk);
1106 }
Jeff Sharkey48877892015-03-18 11:27:19 -07001107 break;
1108 }
San Mehat4270e1e2010-01-29 05:32:19 -08001109
Jeff Sharkey48877892015-03-18 11:27:19 -07001110 case VoldResponseCode.VOLUME_CREATED: {
Jeff Sharkey48877892015-03-18 11:27:19 -07001111 final String id = cooked[1];
1112 final int type = Integer.parseInt(cooked[2]);
Jeff Sharkey5cc0df22015-06-17 19:44:05 -07001113 final String diskId = TextUtils.nullIfEmpty(cooked[3]);
1114 final String partGuid = TextUtils.nullIfEmpty(cooked[4]);
1115
Jeff Sharkey27de30d2015-04-18 16:20:27 -07001116 final DiskInfo disk = mDisks.get(diskId);
Jeff Sharkey5af1835d2015-07-07 17:26:59 -07001117 final VolumeInfo vol = new VolumeInfo(id, type, disk, partGuid);
Jeff Sharkey48877892015-03-18 11:27:19 -07001118 mVolumes.put(id, vol);
1119 onVolumeCreatedLocked(vol);
1120 break;
1121 }
1122 case VoldResponseCode.VOLUME_STATE_CHANGED: {
1123 if (cooked.length != 3) break;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001124 final VolumeInfo vol = mVolumes.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -07001125 if (vol != null) {
1126 final int oldState = vol.state;
1127 final int newState = Integer.parseInt(cooked[2]);
1128 vol.state = newState;
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001129 onVolumeStateChangedLocked(vol, oldState, newState);
Jeff Sharkey48877892015-03-18 11:27:19 -07001130 }
1131 break;
1132 }
1133 case VoldResponseCode.VOLUME_FS_TYPE_CHANGED: {
1134 if (cooked.length != 3) break;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001135 final VolumeInfo vol = mVolumes.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -07001136 if (vol != null) {
1137 vol.fsType = cooked[2];
1138 }
1139 break;
1140 }
1141 case VoldResponseCode.VOLUME_FS_UUID_CHANGED: {
1142 if (cooked.length != 3) break;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001143 final VolumeInfo vol = mVolumes.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -07001144 if (vol != null) {
1145 vol.fsUuid = cooked[2];
1146 }
1147 break;
1148 }
1149 case VoldResponseCode.VOLUME_FS_LABEL_CHANGED: {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001150 final VolumeInfo vol = mVolumes.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -07001151 if (vol != null) {
Jeff Sharkey27de30d2015-04-18 16:20:27 -07001152 final StringBuilder builder = new StringBuilder();
1153 for (int i = 2; i < cooked.length; i++) {
1154 builder.append(cooked[i]).append(' ');
1155 }
1156 vol.fsLabel = builder.toString().trim();
Jeff Sharkey48877892015-03-18 11:27:19 -07001157 }
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001158 // TODO: notify listeners that label changed
Jeff Sharkey48877892015-03-18 11:27:19 -07001159 break;
1160 }
1161 case VoldResponseCode.VOLUME_PATH_CHANGED: {
1162 if (cooked.length != 3) break;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001163 final VolumeInfo vol = mVolumes.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -07001164 if (vol != null) {
1165 vol.path = cooked[2];
1166 }
1167 break;
1168 }
Jeff Sharkey50a05452015-04-29 11:24:52 -07001169 case VoldResponseCode.VOLUME_INTERNAL_PATH_CHANGED: {
1170 if (cooked.length != 3) break;
1171 final VolumeInfo vol = mVolumes.get(cooked[1]);
1172 if (vol != null) {
1173 vol.internalPath = cooked[2];
1174 }
1175 break;
1176 }
Jeff Sharkey48877892015-03-18 11:27:19 -07001177 case VoldResponseCode.VOLUME_DESTROYED: {
1178 if (cooked.length != 2) break;
1179 mVolumes.remove(cooked[1]);
1180 break;
1181 }
San Mehat4270e1e2010-01-29 05:32:19 -08001182
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001183 case VoldResponseCode.MOVE_STATUS: {
1184 final int status = Integer.parseInt(cooked[1]);
1185 onMoveStatusLocked(status);
1186 break;
1187 }
Jeff Sharkey9756d752015-05-14 21:07:42 -07001188 case VoldResponseCode.BENCHMARK_RESULT: {
Jeff Sharkeye8a4b662015-06-27 15:43:45 -07001189 if (cooked.length != 7) break;
1190 final String path = cooked[1];
1191 final String ident = cooked[2];
1192 final long create = Long.parseLong(cooked[3]);
1193 final long drop = Long.parseLong(cooked[4]);
1194 final long run = Long.parseLong(cooked[5]);
1195 final long destroy = Long.parseLong(cooked[6]);
1196
Jeff Sharkey9756d752015-05-14 21:07:42 -07001197 final DropBoxManager dropBox = mContext.getSystemService(DropBoxManager.class);
Jeff Sharkeye8a4b662015-06-27 15:43:45 -07001198 dropBox.addText(TAG_STORAGE_BENCHMARK, scrubPath(path)
1199 + " " + ident + " " + create + " " + run + " " + destroy);
1200
1201 final VolumeRecord rec = findRecordForPath(path);
1202 if (rec != null) {
1203 rec.lastBenchMillis = System.currentTimeMillis();
1204 writeSettingsLocked();
1205 }
1206
1207 break;
1208 }
1209 case VoldResponseCode.TRIM_RESULT: {
1210 if (cooked.length != 4) break;
1211 final String path = cooked[1];
1212 final long bytes = Long.parseLong(cooked[2]);
1213 final long time = Long.parseLong(cooked[3]);
1214
1215 final DropBoxManager dropBox = mContext.getSystemService(DropBoxManager.class);
1216 dropBox.addText(TAG_STORAGE_TRIM, scrubPath(path)
1217 + " " + bytes + " " + time);
1218
1219 final VolumeRecord rec = findRecordForPath(path);
1220 if (rec != null) {
1221 rec.lastTrimMillis = System.currentTimeMillis();
1222 writeSettingsLocked();
1223 }
1224
Jeff Sharkey9756d752015-05-14 21:07:42 -07001225 break;
1226 }
1227
Jeff Sharkey48877892015-03-18 11:27:19 -07001228 default: {
1229 Slog.d(TAG, "Unhandled vold event " + code);
Mike Lockwooda5250c92011-05-23 13:44:04 -04001230 }
San Mehat4270e1e2010-01-29 05:32:19 -08001231 }
1232
Daniel Sandler5f27ef42010-03-16 15:42:02 -04001233 return true;
San Mehat4270e1e2010-01-29 05:32:19 -08001234 }
1235
Jeff Sharkeyeba260d2015-04-19 14:35:16 -07001236 private void onDiskScannedLocked(DiskInfo disk) {
Jeff Sharkey620b32b2015-04-23 19:36:02 -07001237 int volumeCount = 0;
Jeff Sharkeyeba260d2015-04-19 14:35:16 -07001238 for (int i = 0; i < mVolumes.size(); i++) {
1239 final VolumeInfo vol = mVolumes.valueAt(i);
1240 if (Objects.equals(disk.id, vol.getDiskId())) {
Jeff Sharkey620b32b2015-04-23 19:36:02 -07001241 volumeCount++;
Jeff Sharkeyeba260d2015-04-19 14:35:16 -07001242 }
1243 }
1244
Jeff Sharkeyc7acac62015-06-12 16:16:56 -07001245 final Intent intent = new Intent(DiskInfo.ACTION_DISK_SCANNED);
Jeff Sharkey7732e1e2016-03-30 17:14:23 -06001246 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
1247 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
Jeff Sharkeyc7acac62015-06-12 16:16:56 -07001248 intent.putExtra(DiskInfo.EXTRA_DISK_ID, disk.id);
1249 intent.putExtra(DiskInfo.EXTRA_VOLUME_COUNT, volumeCount);
Jeff Sharkeyabc3e852015-08-03 14:41:13 -07001250 mHandler.obtainMessage(H_INTERNAL_BROADCAST, intent).sendToTarget();
Jeff Sharkeyc7acac62015-06-12 16:16:56 -07001251
1252 final CountDownLatch latch = mDiskScanLatches.remove(disk.id);
1253 if (latch != null) {
1254 latch.countDown();
1255 }
1256
Jeff Sharkeyf5a6bd72015-05-19 14:42:38 -07001257 disk.volumeCount = volumeCount;
Jeff Sharkey620b32b2015-04-23 19:36:02 -07001258 mCallbacks.notifyDiskScanned(disk, volumeCount);
Jeff Sharkeyeba260d2015-04-19 14:35:16 -07001259 }
1260
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001261 private void onVolumeCreatedLocked(VolumeInfo vol) {
Jeff Sharkey6855c482016-03-31 14:34:38 -06001262 if (mPms.isOnlyCoreApps()) {
1263 Slog.d(TAG, "System booted in core-only mode; ignoring volume " + vol.getId());
1264 return;
1265 }
1266
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001267 if (vol.type == VolumeInfo.TYPE_EMULATED) {
1268 final StorageManager storage = mContext.getSystemService(StorageManager.class);
1269 final VolumeInfo privateVol = storage.findPrivateForEmulated(vol);
1270
1271 if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, mPrimaryStorageUuid)
1272 && VolumeInfo.ID_PRIVATE_INTERNAL.equals(privateVol.id)) {
1273 Slog.v(TAG, "Found primary storage at " + vol);
1274 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
1275 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
1276 mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
1277
1278 } else if (Objects.equals(privateVol.fsUuid, mPrimaryStorageUuid)) {
1279 Slog.v(TAG, "Found primary storage at " + vol);
1280 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
1281 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
1282 mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
1283 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001284
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001285 } else if (vol.type == VolumeInfo.TYPE_PUBLIC) {
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001286 // TODO: only look at first public partition
1287 if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, mPrimaryStorageUuid)
1288 && vol.disk.isDefaultPrimary()) {
1289 Slog.v(TAG, "Found primary storage at " + vol);
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07001290 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
1291 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
San Mehat4270e1e2010-01-29 05:32:19 -08001292 }
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07001293
1294 // Adoptable public disks are visible to apps, since they meet
1295 // public API requirement of being in a stable location.
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001296 if (vol.disk.isAdoptable()) {
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07001297 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
1298 }
1299
Jeff Sharkeyab15c392016-05-05 11:45:01 -06001300 vol.mountUserId = mCurrentUserId;
Jeff Sharkey48877892015-03-18 11:27:19 -07001301 mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
San Mehat4270e1e2010-01-29 05:32:19 -08001302
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -07001303 } else if (vol.type == VolumeInfo.TYPE_PRIVATE) {
1304 mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
1305
San Mehat4270e1e2010-01-29 05:32:19 -08001306 } else {
Jeff Sharkey48877892015-03-18 11:27:19 -07001307 Slog.d(TAG, "Skipping automatic mounting of " + vol);
San Mehat4270e1e2010-01-29 05:32:19 -08001308 }
1309 }
1310
Jeff Sharkeye6c04f92015-04-18 21:38:05 -07001311 private boolean isBroadcastWorthy(VolumeInfo vol) {
1312 switch (vol.getType()) {
Jeff Sharkeyc7acac62015-06-12 16:16:56 -07001313 case VolumeInfo.TYPE_PRIVATE:
Jeff Sharkeye6c04f92015-04-18 21:38:05 -07001314 case VolumeInfo.TYPE_PUBLIC:
1315 case VolumeInfo.TYPE_EMULATED:
1316 break;
1317 default:
1318 return false;
1319 }
1320
1321 switch (vol.getState()) {
1322 case VolumeInfo.STATE_MOUNTED:
1323 case VolumeInfo.STATE_MOUNTED_READ_ONLY:
1324 case VolumeInfo.STATE_EJECTING:
1325 case VolumeInfo.STATE_UNMOUNTED:
Jeff Sharkeyc7acac62015-06-12 16:16:56 -07001326 case VolumeInfo.STATE_UNMOUNTABLE:
Tony Mantlerf0d71052015-06-24 11:45:25 -07001327 case VolumeInfo.STATE_BAD_REMOVAL:
Jeff Sharkeye6c04f92015-04-18 21:38:05 -07001328 break;
1329 default:
1330 return false;
1331 }
1332
1333 return true;
1334 }
1335
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001336 private void onVolumeStateChangedLocked(VolumeInfo vol, int oldState, int newState) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001337 // Remember that we saw this volume so we're ready to accept user
1338 // metadata, or so we can annoy them when a private volume is ejected
1339 if (vol.isMountedReadable() && !TextUtils.isEmpty(vol.fsUuid)) {
Jeff Sharkey5cc0df22015-06-17 19:44:05 -07001340 VolumeRecord rec = mRecords.get(vol.fsUuid);
1341 if (rec == null) {
1342 rec = new VolumeRecord(vol.type, vol.fsUuid);
1343 rec.partGuid = vol.partGuid;
Jeff Sharkeye8a4b662015-06-27 15:43:45 -07001344 rec.createdMillis = System.currentTimeMillis();
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001345 if (vol.type == VolumeInfo.TYPE_PRIVATE) {
1346 rec.nickname = vol.disk.getDescription();
1347 }
1348 mRecords.put(rec.fsUuid, rec);
1349 writeSettingsLocked();
Jeff Sharkey5cc0df22015-06-17 19:44:05 -07001350 } else {
1351 // Handle upgrade case where we didn't store partition GUID
1352 if (TextUtils.isEmpty(rec.partGuid)) {
1353 rec.partGuid = vol.partGuid;
1354 writeSettingsLocked();
1355 }
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001356 }
1357 }
1358
Jeff Sharkeye6c04f92015-04-18 21:38:05 -07001359 mCallbacks.notifyVolumeStateChanged(vol, oldState, newState);
1360
Yasuhiro Matsuda87a38b52015-07-24 22:10:16 +09001361 // Do not broadcast before boot has completed to avoid launching the
1362 // processes that receive the intent unnecessarily.
1363 if (mBootCompleted && isBroadcastWorthy(vol)) {
Jeff Sharkeye6c04f92015-04-18 21:38:05 -07001364 final Intent intent = new Intent(VolumeInfo.ACTION_VOLUME_STATE_CHANGED);
Jeff Sharkeyc7acac62015-06-12 16:16:56 -07001365 intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.id);
1366 intent.putExtra(VolumeInfo.EXTRA_VOLUME_STATE, newState);
Tony Mantlerf0d71052015-06-24 11:45:25 -07001367 intent.putExtra(VolumeRecord.EXTRA_FS_UUID, vol.fsUuid);
Jeff Sharkey7732e1e2016-03-30 17:14:23 -06001368 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
1369 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
Jeff Sharkeyabc3e852015-08-03 14:41:13 -07001370 mHandler.obtainMessage(H_INTERNAL_BROADCAST, intent).sendToTarget();
Jeff Sharkeye6c04f92015-04-18 21:38:05 -07001371 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001372
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001373 final String oldStateEnv = VolumeInfo.getEnvironmentForState(oldState);
1374 final String newStateEnv = VolumeInfo.getEnvironmentForState(newState);
Emily Bernier92aa5a22014-07-07 10:11:48 -04001375
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001376 if (!Objects.equals(oldStateEnv, newStateEnv)) {
1377 // Kick state changed event towards all started users. Any users
1378 // started after this point will trigger additional
1379 // user-specific broadcasts.
Jeff Sharkeyce14cd02015-12-07 15:35:42 -07001380 for (int userId : mSystemUnlockedUsers) {
Jeff Sharkey46349872015-07-28 10:49:47 -07001381 if (vol.isVisibleForRead(userId)) {
Svet Ganov6ee871e2015-07-10 14:29:33 -07001382 final StorageVolume userVol = vol.buildStorageVolume(mContext, userId, false);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001383 mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget();
Jeff Sharkey48877892015-03-18 11:27:19 -07001384
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001385 mCallbacks.notifyStorageStateChanged(userVol.getPath(), oldStateEnv,
1386 newStateEnv);
San Mehat4270e1e2010-01-29 05:32:19 -08001387 }
1388 }
1389 }
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001390
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07001391 if (vol.type == VolumeInfo.TYPE_PUBLIC && vol.state == VolumeInfo.STATE_EJECTING) {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001392 // TODO: this should eventually be handled by new ObbVolume state changes
1393 /*
1394 * Some OBBs might have been unmounted when this volume was
1395 * unmounted, so send a message to the handler to let it know to
1396 * remove those from the list of mounted OBBS.
1397 */
1398 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(
1399 OBB_FLUSH_MOUNT_STATE, vol.path));
1400 }
San Mehat4270e1e2010-01-29 05:32:19 -08001401 }
1402
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001403 private void onMoveStatusLocked(int status) {
1404 if (mMoveCallback == null) {
1405 Slog.w(TAG, "Odd, status but no move requested");
1406 return;
1407 }
1408
1409 // TODO: estimate remaining time
1410 try {
Jeff Sharkey50a05452015-04-29 11:24:52 -07001411 mMoveCallback.onStatusChanged(-1, status, -1);
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001412 } catch (RemoteException ignored) {
1413 }
1414
1415 // We've finished copying and we're about to clean up old data, so
1416 // remember that move was successful if we get rebooted
1417 if (status == MOVE_STATUS_COPY_FINISHED) {
1418 Slog.d(TAG, "Move to " + mMoveTargetUuid + " copy phase finshed; persisting");
1419
1420 mPrimaryStorageUuid = mMoveTargetUuid;
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001421 writeSettingsLocked();
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001422 }
1423
1424 if (PackageManager.isMoveStatusFinished(status)) {
1425 Slog.d(TAG, "Move to " + mMoveTargetUuid + " finished with status " + status);
1426
1427 mMoveCallback = null;
1428 mMoveTargetUuid = null;
1429 }
1430 }
1431
Jeff Sharkey48877892015-03-18 11:27:19 -07001432 private void enforcePermission(String perm) {
1433 mContext.enforceCallingOrSelfPermission(perm, perm);
Mike Lockwooda5250c92011-05-23 13:44:04 -04001434 }
1435
Jeff Sharkey2e606d72015-07-27 14:19:54 -07001436 /**
1437 * Decide if volume is mountable per device policies.
1438 */
1439 private boolean isMountDisallowed(VolumeInfo vol) {
Philip P. Moltmann5201f1e2016-09-30 14:58:27 -07001440 UserManager userManager = mContext.getSystemService(UserManager.class);
1441
1442 boolean isUsbRestricted = false;
1443 if (vol.disk != null && vol.disk.isUsb()) {
1444 isUsbRestricted = userManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER,
Jeff Sharkey2e606d72015-07-27 14:19:54 -07001445 Binder.getCallingUserHandle());
Emily Bernier92aa5a22014-07-07 10:11:48 -04001446 }
Philip P. Moltmann5201f1e2016-09-30 14:58:27 -07001447
1448 boolean isTypeRestricted = false;
1449 if (vol.type == VolumeInfo.TYPE_PUBLIC || vol.type == VolumeInfo.TYPE_PRIVATE) {
1450 isTypeRestricted = userManager
1451 .hasUserRestriction(UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA,
1452 Binder.getCallingUserHandle());
1453 }
1454
1455 return isUsbRestricted || isTypeRestricted;
Emily Bernier92aa5a22014-07-07 10:11:48 -04001456 }
1457
Amith Yamasani462ac3a2015-06-30 14:21:01 -07001458 private void enforceAdminUser() {
1459 UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
1460 final int callingUserId = UserHandle.getCallingUserId();
1461 boolean isAdmin;
1462 long token = Binder.clearCallingIdentity();
1463 try {
1464 isAdmin = um.getUserInfo(callingUserId).isAdmin();
1465 } finally {
1466 Binder.restoreCallingIdentity(token);
1467 }
1468 if (!isAdmin) {
1469 throw new SecurityException("Only admin users can adopt sd cards");
1470 }
1471 }
1472
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001473 /**
San Mehat207e5382010-02-04 20:46:54 -08001474 * Constructs a new MountService instance
1475 *
1476 * @param context Binder context for this service
1477 */
1478 public MountService(Context context) {
Christopher Tated417d622013-08-19 16:14:25 -07001479 sSelf = this;
1480
San Mehat207e5382010-02-04 20:46:54 -08001481 mContext = context;
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001482 mCallbacks = new Callbacks(FgThread.get().getLooper());
Jeff Sharkeyb9fe5372015-12-03 15:23:08 -07001483 mLockPatternUtils = new LockPatternUtils(mContext);
San Mehat207e5382010-02-04 20:46:54 -08001484
San Mehat207e5382010-02-04 20:46:54 -08001485 // XXX: This will go away soon in favor of IMountServiceObserver
1486 mPms = (PackageManagerService) ServiceManager.getService("package");
1487
Dianne Hackbornefa92b22013-05-03 14:11:43 -07001488 HandlerThread hthread = new HandlerThread(TAG);
1489 hthread.start();
1490 mHandler = new MountServiceHandler(hthread.getLooper());
Daniel Sandler5f27ef42010-03-16 15:42:02 -04001491
Kenny Roota02b8b02010-08-05 16:14:17 -07001492 // Add OBB Action Handler to MountService thread.
Dianne Hackborn8d044e82013-04-30 17:24:15 -07001493 mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper());
Kenny Roota02b8b02010-08-05 16:14:17 -07001494
Christopher Tate7265abe2014-11-21 13:54:45 -08001495 // Initialize the last-fstrim tracking if necessary
1496 File dataDir = Environment.getDataDirectory();
1497 File systemDir = new File(dataDir, "system");
1498 mLastMaintenanceFile = new File(systemDir, LAST_FSTRIM_FILE);
1499 if (!mLastMaintenanceFile.exists()) {
1500 // Not setting mLastMaintenance here means that we will force an
1501 // fstrim during reboot following the OTA that installs this code.
1502 try {
1503 (new FileOutputStream(mLastMaintenanceFile)).close();
1504 } catch (IOException e) {
1505 Slog.e(TAG, "Unable to create fstrim record " + mLastMaintenanceFile.getPath());
1506 }
1507 } else {
1508 mLastMaintenance = mLastMaintenanceFile.lastModified();
1509 }
1510
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001511 mSettingsFile = new AtomicFile(
Jeff Sharkey8212ae02016-02-10 14:46:43 -07001512 new File(Environment.getDataSystemDirectory(), "storage.xml"));
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001513
1514 synchronized (mLock) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001515 readSettingsLocked();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001516 }
1517
Svet Ganov6ee871e2015-07-10 14:29:33 -07001518 LocalServices.addService(MountServiceInternal.class, mMountServiceInternal);
1519
Marco Nelissenc34ebce2010-02-18 13:39:41 -08001520 /*
Kenny Root305bcbf2010-09-03 07:56:38 -07001521 * Create the connection to vold with a maximum queue of twice the
1522 * amount of containers we'd ever expect to have. This keeps an
1523 * "asec list" from blocking a thread repeatedly.
1524 */
Paul Lawrence1c62cbb2015-06-03 14:14:52 -07001525
Dianne Hackborn77b987f2014-02-26 16:20:52 -08001526 mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25,
1527 null);
Jeff Sharkey48877892015-03-18 11:27:19 -07001528 mConnector.setDebug(true);
Jeff Sharkey8948c012015-11-03 12:33:54 -08001529 mConnector.setWarnIfHeld(mLock);
Jeff Sharkeycd575992016-03-29 14:12:49 -06001530 mConnectorThread = new Thread(mConnector, VOLD_TAG);
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -07001531
Paul Lawrence1c62cbb2015-06-03 14:14:52 -07001532 // Reuse parameters from first connector since they are tested and safe
1533 mCryptConnector = new NativeDaemonConnector(this, "cryptd",
1534 MAX_CONTAINERS * 2, CRYPTD_TAG, 25, null);
1535 mCryptConnector.setDebug(true);
Jeff Sharkeycd575992016-03-29 14:12:49 -06001536 mCryptConnectorThread = new Thread(mCryptConnector, CRYPTD_TAG);
Paul Lawrence1c62cbb2015-06-03 14:14:52 -07001537
Jeff Sharkeybcd262d2015-06-10 09:41:17 -07001538 final IntentFilter userFilter = new IntentFilter();
1539 userFilter.addAction(Intent.ACTION_USER_ADDED);
1540 userFilter.addAction(Intent.ACTION_USER_REMOVED);
1541 mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);
1542
Jeff Sharkey5a9bb742015-11-03 10:15:57 -08001543 synchronized (mLock) {
1544 addInternalVolumeLocked();
1545 }
Amith Yamasania7892482015-08-07 11:09:05 -07001546
Kenny Root07714d42011-08-17 17:49:28 -07001547 // Add ourself to the Watchdog monitors if enabled.
1548 if (WATCHDOG_ENABLE) {
1549 Watchdog.getInstance().addMonitor(this);
1550 }
San Mehat207e5382010-02-04 20:46:54 -08001551 }
1552
Jeff Sharkeycd575992016-03-29 14:12:49 -06001553 private void start() {
1554 mConnectorThread.start();
1555 mCryptConnectorThread.start();
1556 }
1557
Jeff Sharkey56e62932015-03-21 20:41:00 -07001558 private void systemReady() {
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001559 mSystemReady = true;
1560 mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget();
1561 }
1562
Yasuhiro Matsuda87a38b52015-07-24 22:10:16 +09001563 private void bootCompleted() {
1564 mBootCompleted = true;
1565 }
1566
Jeff Sharkeyfced5342015-05-10 14:53:34 -07001567 private String getDefaultPrimaryStorageUuid() {
1568 if (SystemProperties.getBoolean(StorageManager.PROP_PRIMARY_PHYSICAL, false)) {
1569 return StorageManager.UUID_PRIMARY_PHYSICAL;
1570 } else {
1571 return StorageManager.UUID_PRIVATE_INTERNAL;
1572 }
1573 }
1574
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001575 private void readSettingsLocked() {
1576 mRecords.clear();
Jeff Sharkeyfced5342015-05-10 14:53:34 -07001577 mPrimaryStorageUuid = getDefaultPrimaryStorageUuid();
Jeff Sharkey4c099d02015-05-15 13:45:00 -07001578 mForceAdoptable = false;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001579
1580 FileInputStream fis = null;
1581 try {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001582 fis = mSettingsFile.openRead();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001583 final XmlPullParser in = Xml.newPullParser();
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +01001584 in.setInput(fis, StandardCharsets.UTF_8.name());
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001585
1586 int type;
1587 while ((type = in.next()) != END_DOCUMENT) {
1588 if (type == START_TAG) {
1589 final String tag = in.getName();
Jeff Sharkey620b32b2015-04-23 19:36:02 -07001590 if (TAG_VOLUMES.equals(tag)) {
1591 final int version = readIntAttribute(in, ATTR_VERSION, VERSION_INIT);
Jeff Sharkeyfced5342015-05-10 14:53:34 -07001592 final boolean primaryPhysical = SystemProperties.getBoolean(
1593 StorageManager.PROP_PRIMARY_PHYSICAL, false);
1594 final boolean validAttr = (version >= VERSION_FIX_PRIMARY)
1595 || (version >= VERSION_ADD_PRIMARY && !primaryPhysical);
1596 if (validAttr) {
Jeff Sharkey620b32b2015-04-23 19:36:02 -07001597 mPrimaryStorageUuid = readStringAttribute(in,
1598 ATTR_PRIMARY_STORAGE_UUID);
Jeff Sharkey620b32b2015-04-23 19:36:02 -07001599 }
Jeff Sharkey4c099d02015-05-15 13:45:00 -07001600 mForceAdoptable = readBooleanAttribute(in, ATTR_FORCE_ADOPTABLE, false);
Jeff Sharkey620b32b2015-04-23 19:36:02 -07001601
1602 } else if (TAG_VOLUME.equals(tag)) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001603 final VolumeRecord rec = readVolumeRecord(in);
1604 mRecords.put(rec.fsUuid, rec);
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001605 }
1606 }
1607 }
1608 } catch (FileNotFoundException e) {
1609 // Missing metadata is okay, probably first boot
1610 } catch (IOException e) {
1611 Slog.wtf(TAG, "Failed reading metadata", e);
1612 } catch (XmlPullParserException e) {
1613 Slog.wtf(TAG, "Failed reading metadata", e);
1614 } finally {
1615 IoUtils.closeQuietly(fis);
1616 }
1617 }
1618
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001619 private void writeSettingsLocked() {
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001620 FileOutputStream fos = null;
1621 try {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001622 fos = mSettingsFile.startWrite();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001623
1624 XmlSerializer out = new FastXmlSerializer();
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +01001625 out.setOutput(fos, StandardCharsets.UTF_8.name());
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001626 out.startDocument(null, true);
1627 out.startTag(null, TAG_VOLUMES);
Jeff Sharkeyfced5342015-05-10 14:53:34 -07001628 writeIntAttribute(out, ATTR_VERSION, VERSION_FIX_PRIMARY);
Jeff Sharkey620b32b2015-04-23 19:36:02 -07001629 writeStringAttribute(out, ATTR_PRIMARY_STORAGE_UUID, mPrimaryStorageUuid);
Jeff Sharkey4c099d02015-05-15 13:45:00 -07001630 writeBooleanAttribute(out, ATTR_FORCE_ADOPTABLE, mForceAdoptable);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001631 final int size = mRecords.size();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001632 for (int i = 0; i < size; i++) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001633 final VolumeRecord rec = mRecords.valueAt(i);
1634 writeVolumeRecord(out, rec);
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001635 }
1636 out.endTag(null, TAG_VOLUMES);
1637 out.endDocument();
1638
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001639 mSettingsFile.finishWrite(fos);
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001640 } catch (IOException e) {
1641 if (fos != null) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001642 mSettingsFile.failWrite(fos);
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001643 }
1644 }
1645 }
1646
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001647 public static VolumeRecord readVolumeRecord(XmlPullParser in) throws IOException {
1648 final int type = readIntAttribute(in, ATTR_TYPE);
1649 final String fsUuid = readStringAttribute(in, ATTR_FS_UUID);
1650 final VolumeRecord meta = new VolumeRecord(type, fsUuid);
Jeff Sharkey5cc0df22015-06-17 19:44:05 -07001651 meta.partGuid = readStringAttribute(in, ATTR_PART_GUID);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001652 meta.nickname = readStringAttribute(in, ATTR_NICKNAME);
1653 meta.userFlags = readIntAttribute(in, ATTR_USER_FLAGS);
Jeff Sharkeye8a4b662015-06-27 15:43:45 -07001654 meta.createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS);
1655 meta.lastTrimMillis = readLongAttribute(in, ATTR_LAST_TRIM_MILLIS);
1656 meta.lastBenchMillis = readLongAttribute(in, ATTR_LAST_BENCH_MILLIS);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001657 return meta;
1658 }
1659
1660 public static void writeVolumeRecord(XmlSerializer out, VolumeRecord rec) throws IOException {
1661 out.startTag(null, TAG_VOLUME);
1662 writeIntAttribute(out, ATTR_TYPE, rec.type);
1663 writeStringAttribute(out, ATTR_FS_UUID, rec.fsUuid);
Jeff Sharkey5cc0df22015-06-17 19:44:05 -07001664 writeStringAttribute(out, ATTR_PART_GUID, rec.partGuid);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001665 writeStringAttribute(out, ATTR_NICKNAME, rec.nickname);
1666 writeIntAttribute(out, ATTR_USER_FLAGS, rec.userFlags);
Jeff Sharkeye8a4b662015-06-27 15:43:45 -07001667 writeLongAttribute(out, ATTR_CREATED_MILLIS, rec.createdMillis);
1668 writeLongAttribute(out, ATTR_LAST_TRIM_MILLIS, rec.lastTrimMillis);
1669 writeLongAttribute(out, ATTR_LAST_BENCH_MILLIS, rec.lastBenchMillis);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001670 out.endTag(null, TAG_VOLUME);
1671 }
1672
San Mehat207e5382010-02-04 20:46:54 -08001673 /**
San Mehat4270e1e2010-01-29 05:32:19 -08001674 * Exposed API calls below here
1675 */
1676
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001677 @Override
San Mehat4270e1e2010-01-29 05:32:19 -08001678 public void registerListener(IMountServiceListener listener) {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001679 mCallbacks.register(listener);
San Mehat4270e1e2010-01-29 05:32:19 -08001680 }
1681
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001682 @Override
San Mehat4270e1e2010-01-29 05:32:19 -08001683 public void unregisterListener(IMountServiceListener listener) {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001684 mCallbacks.unregister(listener);
San Mehat4270e1e2010-01-29 05:32:19 -08001685 }
1686
Jeff Sharkey48877892015-03-18 11:27:19 -07001687 @Override
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -08001688 public void shutdown(final IMountShutdownObserver observer) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001689 enforcePermission(android.Manifest.permission.SHUTDOWN);
San Mehat4270e1e2010-01-29 05:32:19 -08001690
San Mehata5078592010-03-25 09:36:54 -07001691 Slog.i(TAG, "Shutting down");
Jeff Sharkey48877892015-03-18 11:27:19 -07001692 mHandler.obtainMessage(H_SHUTDOWN, observer).sendToTarget();
San Mehat4270e1e2010-01-29 05:32:19 -08001693 }
1694
Jeff Sharkey48877892015-03-18 11:27:19 -07001695 @Override
San Mehatb1043402010-02-05 08:26:50 -08001696 public boolean isUsbMassStorageConnected() {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001697 throw new UnsupportedOperationException();
San Mehatb1043402010-02-05 08:26:50 -08001698 }
1699
Jeff Sharkey48877892015-03-18 11:27:19 -07001700 @Override
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001701 public void setUsbMassStorageEnabled(boolean enable) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001702 throw new UnsupportedOperationException();
San Mehatb1043402010-02-05 08:26:50 -08001703 }
1704
Jeff Sharkey48877892015-03-18 11:27:19 -07001705 @Override
San Mehatb1043402010-02-05 08:26:50 -08001706 public boolean isUsbMassStorageEnabled() {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001707 throw new UnsupportedOperationException();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001708 }
Jason parks9ed98bc2011-01-17 09:58:35 -06001709
Jeff Sharkey48877892015-03-18 11:27:19 -07001710 @Override
San Mehat4270e1e2010-01-29 05:32:19 -08001711 public String getVolumeState(String mountPoint) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001712 throw new UnsupportedOperationException();
San Mehat7fd0fee2009-12-17 07:12:23 -08001713 }
1714
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001715 @Override
Kenny Roote1ff2142010-10-12 11:20:01 -07001716 public boolean isExternalStorageEmulated() {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001717 throw new UnsupportedOperationException();
Kenny Roote1ff2142010-10-12 11:20:01 -07001718 }
1719
Jeff Sharkey48877892015-03-18 11:27:19 -07001720 @Override
San Mehat4270e1e2010-01-29 05:32:19 -08001721 public int mountVolume(String path) {
Jeff Sharkeyef10ee02015-07-05 14:17:27 -07001722 mount(findVolumeIdForPathOrThrow(path));
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001723 return 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001724 }
1725
Jeff Sharkey48877892015-03-18 11:27:19 -07001726 @Override
Ben Komalo13c71972011-09-07 16:35:56 -07001727 public void unmountVolume(String path, boolean force, boolean removeEncryption) {
Jeff Sharkeyef10ee02015-07-05 14:17:27 -07001728 unmount(findVolumeIdForPathOrThrow(path));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001729 }
1730
Jeff Sharkey48877892015-03-18 11:27:19 -07001731 @Override
San Mehat4270e1e2010-01-29 05:32:19 -08001732 public int formatVolume(String path) {
Jeff Sharkeyef10ee02015-07-05 14:17:27 -07001733 format(findVolumeIdForPathOrThrow(path));
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001734 return 0;
1735 }
1736
1737 @Override
1738 public void mount(String volId) {
1739 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1740 waitForReady();
1741
Jeff Sharkeyef10ee02015-07-05 14:17:27 -07001742 final VolumeInfo vol = findVolumeByIdOrThrow(volId);
Jeff Sharkey2e606d72015-07-27 14:19:54 -07001743 if (isMountDisallowed(vol)) {
1744 throw new SecurityException("Mounting " + volId + " restricted by policy");
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001745 }
1746 try {
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07001747 mConnector.execute("volume", "mount", vol.id, vol.mountFlags, vol.mountUserId);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001748 } catch (NativeDaemonConnectorException e) {
1749 throw e.rethrowAsParcelableException();
1750 }
1751 }
1752
1753 @Override
1754 public void unmount(String volId) {
1755 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1756 waitForReady();
1757
Jeff Sharkeyef10ee02015-07-05 14:17:27 -07001758 final VolumeInfo vol = findVolumeByIdOrThrow(volId);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001759
1760 // TODO: expand PMS to know about multiple volumes
Jeff Sharkey275e3e42015-04-24 16:10:32 -07001761 if (vol.isPrimaryPhysical()) {
1762 final long ident = Binder.clearCallingIdentity();
1763 try {
1764 synchronized (mUnmountLock) {
1765 mUnmountSignal = new CountDownLatch(1);
1766 mPms.updateExternalMediaStatus(false, true);
1767 waitForLatch(mUnmountSignal, "mUnmountSignal");
1768 mUnmountSignal = null;
1769 }
1770 } finally {
1771 Binder.restoreCallingIdentity(ident);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001772 }
1773 }
1774
1775 try {
1776 mConnector.execute("volume", "unmount", vol.id);
1777 } catch (NativeDaemonConnectorException e) {
1778 throw e.rethrowAsParcelableException();
1779 }
1780 }
1781
1782 @Override
1783 public void format(String volId) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001784 enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
San Mehat207e5382010-02-04 20:46:54 -08001785 waitForReady();
San Mehat5b77dab2010-01-26 13:28:50 -08001786
Jeff Sharkeyef10ee02015-07-05 14:17:27 -07001787 final VolumeInfo vol = findVolumeByIdOrThrow(volId);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001788 try {
Jeff Sharkey4e83cc92015-05-27 14:38:39 -07001789 mConnector.execute("volume", "format", vol.id, "auto");
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001790 } catch (NativeDaemonConnectorException e) {
1791 throw e.rethrowAsParcelableException();
Jeff Sharkey48877892015-03-18 11:27:19 -07001792 }
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001793 }
1794
1795 @Override
Jeff Sharkey9756d752015-05-14 21:07:42 -07001796 public long benchmark(String volId) {
1797 enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
1798 waitForReady();
1799
1800 try {
Jeff Sharkey14cbe522015-07-08 14:06:37 -07001801 // TODO: make benchmark async so we don't block other commands
1802 final NativeDaemonEvent res = mConnector.execute(3 * DateUtils.MINUTE_IN_MILLIS,
1803 "volume", "benchmark", volId);
Jeff Sharkey9756d752015-05-14 21:07:42 -07001804 return Long.parseLong(res.getMessage());
Todd Kennedy8101ee62015-06-23 13:35:28 -07001805 } catch (NativeDaemonTimeoutException e) {
1806 return Long.MAX_VALUE;
Jeff Sharkey9756d752015-05-14 21:07:42 -07001807 } catch (NativeDaemonConnectorException e) {
1808 throw e.rethrowAsParcelableException();
1809 }
1810 }
1811
1812 @Override
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001813 public void partitionPublic(String diskId) {
1814 enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
1815 waitForReady();
1816
Jeff Sharkeyeba260d2015-04-19 14:35:16 -07001817 final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001818 try {
1819 mConnector.execute("volume", "partition", diskId, "public");
Jeff Sharkeyedcdaf62015-07-09 09:45:36 -07001820 waitForLatch(latch, "partitionPublic", 3 * DateUtils.MINUTE_IN_MILLIS);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001821 } catch (NativeDaemonConnectorException e) {
1822 throw e.rethrowAsParcelableException();
Jeff Sharkeyedcdaf62015-07-09 09:45:36 -07001823 } catch (TimeoutException e) {
1824 throw new IllegalStateException(e);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001825 }
1826 }
1827
1828 @Override
1829 public void partitionPrivate(String diskId) {
1830 enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
Amith Yamasani462ac3a2015-06-30 14:21:01 -07001831 enforceAdminUser();
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001832 waitForReady();
1833
Jeff Sharkeyeba260d2015-04-19 14:35:16 -07001834 final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001835 try {
1836 mConnector.execute("volume", "partition", diskId, "private");
Jeff Sharkeyedcdaf62015-07-09 09:45:36 -07001837 waitForLatch(latch, "partitionPrivate", 3 * DateUtils.MINUTE_IN_MILLIS);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001838 } catch (NativeDaemonConnectorException e) {
1839 throw e.rethrowAsParcelableException();
Jeff Sharkeyedcdaf62015-07-09 09:45:36 -07001840 } catch (TimeoutException e) {
1841 throw new IllegalStateException(e);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001842 }
1843 }
1844
1845 @Override
1846 public void partitionMixed(String diskId, int ratio) {
1847 enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
Amith Yamasani462ac3a2015-06-30 14:21:01 -07001848 enforceAdminUser();
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001849 waitForReady();
1850
Jeff Sharkeyeba260d2015-04-19 14:35:16 -07001851 final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001852 try {
1853 mConnector.execute("volume", "partition", diskId, "mixed", ratio);
Jeff Sharkeyedcdaf62015-07-09 09:45:36 -07001854 waitForLatch(latch, "partitionMixed", 3 * DateUtils.MINUTE_IN_MILLIS);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001855 } catch (NativeDaemonConnectorException e) {
1856 throw e.rethrowAsParcelableException();
Jeff Sharkeyedcdaf62015-07-09 09:45:36 -07001857 } catch (TimeoutException e) {
1858 throw new IllegalStateException(e);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001859 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001860 }
1861
Jeff Sharkey48877892015-03-18 11:27:19 -07001862 @Override
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001863 public void setVolumeNickname(String fsUuid, String nickname) {
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001864 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1865 waitForReady();
1866
Jeff Sharkey50a05452015-04-29 11:24:52 -07001867 Preconditions.checkNotNull(fsUuid);
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001868 synchronized (mLock) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001869 final VolumeRecord rec = mRecords.get(fsUuid);
1870 rec.nickname = nickname;
Jeff Sharkey50a05452015-04-29 11:24:52 -07001871 mCallbacks.notifyVolumeRecordChanged(rec);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001872 writeSettingsLocked();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001873 }
1874 }
1875
1876 @Override
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001877 public void setVolumeUserFlags(String fsUuid, int flags, int mask) {
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001878 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1879 waitForReady();
1880
Jeff Sharkey50a05452015-04-29 11:24:52 -07001881 Preconditions.checkNotNull(fsUuid);
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001882 synchronized (mLock) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001883 final VolumeRecord rec = mRecords.get(fsUuid);
1884 rec.userFlags = (rec.userFlags & ~mask) | (flags & mask);
Jeff Sharkey50a05452015-04-29 11:24:52 -07001885 mCallbacks.notifyVolumeRecordChanged(rec);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001886 writeSettingsLocked();
1887 }
1888 }
1889
1890 @Override
1891 public void forgetVolume(String fsUuid) {
1892 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1893 waitForReady();
1894
Jeff Sharkey50a05452015-04-29 11:24:52 -07001895 Preconditions.checkNotNull(fsUuid);
Jeff Sharkey5a9bb742015-11-03 10:15:57 -08001896
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001897 synchronized (mLock) {
Jeff Sharkey5cc0df22015-06-17 19:44:05 -07001898 final VolumeRecord rec = mRecords.remove(fsUuid);
1899 if (rec != null && !TextUtils.isEmpty(rec.partGuid)) {
Jeff Sharkey5a9bb742015-11-03 10:15:57 -08001900 mHandler.obtainMessage(H_PARTITION_FORGET, rec.partGuid).sendToTarget();
Jeff Sharkey5cc0df22015-06-17 19:44:05 -07001901 }
1902 mCallbacks.notifyVolumeForgotten(fsUuid);
Jeff Sharkey50a05452015-04-29 11:24:52 -07001903
1904 // If this had been primary storage, revert back to internal and
1905 // reset vold so we bind into new volume into place.
1906 if (Objects.equals(mPrimaryStorageUuid, fsUuid)) {
Jeff Sharkeyfced5342015-05-10 14:53:34 -07001907 mPrimaryStorageUuid = getDefaultPrimaryStorageUuid();
Jeff Sharkey5a9bb742015-11-03 10:15:57 -08001908 mHandler.obtainMessage(H_RESET).sendToTarget();
Jeff Sharkey50a05452015-04-29 11:24:52 -07001909 }
1910
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07001911 writeSettingsLocked();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001912 }
1913 }
1914
Jeff Sharkey7d2af542015-05-12 15:27:15 -07001915 @Override
1916 public void forgetAllVolumes() {
1917 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1918 waitForReady();
1919
Jeff Sharkey50a05452015-04-29 11:24:52 -07001920 synchronized (mLock) {
1921 for (int i = 0; i < mRecords.size(); i++) {
1922 final String fsUuid = mRecords.keyAt(i);
Jeff Sharkey5cc0df22015-06-17 19:44:05 -07001923 final VolumeRecord rec = mRecords.valueAt(i);
1924 if (!TextUtils.isEmpty(rec.partGuid)) {
Jeff Sharkey5a9bb742015-11-03 10:15:57 -08001925 mHandler.obtainMessage(H_PARTITION_FORGET, rec.partGuid).sendToTarget();
Jeff Sharkey5cc0df22015-06-17 19:44:05 -07001926 }
Jeff Sharkey50a05452015-04-29 11:24:52 -07001927 mCallbacks.notifyVolumeForgotten(fsUuid);
1928 }
Jeff Sharkey50a05452015-04-29 11:24:52 -07001929 mRecords.clear();
Jeff Sharkey50a05452015-04-29 11:24:52 -07001930
Jeff Sharkeyfced5342015-05-10 14:53:34 -07001931 if (!Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, mPrimaryStorageUuid)) {
1932 mPrimaryStorageUuid = getDefaultPrimaryStorageUuid();
1933 }
1934
1935 writeSettingsLocked();
Jeff Sharkey5a9bb742015-11-03 10:15:57 -08001936 mHandler.obtainMessage(H_RESET).sendToTarget();
Jeff Sharkey50a05452015-04-29 11:24:52 -07001937 }
1938 }
1939
Jeff Sharkey5cc0df22015-06-17 19:44:05 -07001940 private void forgetPartition(String partGuid) {
1941 try {
1942 mConnector.execute("volume", "forget_partition", partGuid);
1943 } catch (NativeDaemonConnectorException e) {
1944 Slog.w(TAG, "Failed to forget key for " + partGuid + ": " + e);
1945 }
1946 }
1947
Svet Ganov6ee871e2015-07-10 14:29:33 -07001948 private void remountUidExternalStorage(int uid, int mode) {
Jeff Sharkey9527b222015-06-24 15:24:48 -07001949 waitForReady();
1950
Svet Ganov6ee871e2015-07-10 14:29:33 -07001951 String modeName = "none";
1952 switch (mode) {
1953 case Zygote.MOUNT_EXTERNAL_DEFAULT: {
1954 modeName = "default";
1955 } break;
1956
1957 case Zygote.MOUNT_EXTERNAL_READ: {
1958 modeName = "read";
1959 } break;
1960
1961 case Zygote.MOUNT_EXTERNAL_WRITE: {
1962 modeName = "write";
1963 } break;
Jeff Sharkey9527b222015-06-24 15:24:48 -07001964 }
1965
1966 try {
Svet Ganov6ee871e2015-07-10 14:29:33 -07001967 mConnector.execute("volume", "remount_uid", uid, modeName);
Jeff Sharkey9527b222015-06-24 15:24:48 -07001968 } catch (NativeDaemonConnectorException e) {
Svet Ganov6ee871e2015-07-10 14:29:33 -07001969 Slog.w(TAG, "Failed to remount UID " + uid + " as " + modeName + ": " + e);
Jeff Sharkey9527b222015-06-24 15:24:48 -07001970 }
1971 }
1972
1973 @Override
Jeff Sharkey4c099d02015-05-15 13:45:00 -07001974 public void setDebugFlags(int flags, int mask) {
1975 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1976 waitForReady();
1977
Jeff Sharkeyba512352015-11-12 20:17:45 -08001978 if ((mask & StorageManager.DEBUG_EMULATE_FBE) != 0) {
Paul Lawrence20be5d62016-02-26 13:51:17 -08001979 if (StorageManager.isFileEncryptedNativeOnly()) {
Jeff Sharkeyce14cd02015-12-07 15:35:42 -07001980 throw new IllegalStateException(
1981 "Emulation not available on device with native FBE");
1982 }
Jeff Sharkey5a785162016-03-21 13:02:06 -06001983 if (mLockPatternUtils.isCredentialRequiredToDecrypt(false)) {
1984 throw new IllegalStateException(
1985 "Emulation requires disabling 'Secure start-up' in Settings > Security");
1986 }
Jeff Sharkeyce14cd02015-12-07 15:35:42 -07001987
Jeff Sharkey1176e512016-02-29 17:01:26 -07001988 final long token = Binder.clearCallingIdentity();
1989 try {
1990 final boolean emulateFbe = (flags & StorageManager.DEBUG_EMULATE_FBE) != 0;
1991 SystemProperties.set(StorageManager.PROP_EMULATE_FBE, Boolean.toString(emulateFbe));
Jeff Sharkeyce14cd02015-12-07 15:35:42 -07001992
Jeff Sharkey1176e512016-02-29 17:01:26 -07001993 // Perform hard reboot to kick policy into place
1994 mContext.getSystemService(PowerManager.class).reboot(null);
1995 } finally {
1996 Binder.restoreCallingIdentity(token);
1997 }
Jeff Sharkeyba512352015-11-12 20:17:45 -08001998 }
Jeff Sharkey4c099d02015-05-15 13:45:00 -07001999
Jeff Sharkeyba512352015-11-12 20:17:45 -08002000 if ((mask & StorageManager.DEBUG_FORCE_ADOPTABLE) != 0) {
Jeff Sharkey6ed74182016-08-23 13:53:53 -06002001 if (StorageManager.isFileEncryptedNativeOnly()) {
2002 throw new IllegalStateException(
2003 "Adoptable storage not available on device with native FBE");
2004 }
2005
Jeff Sharkeyba512352015-11-12 20:17:45 -08002006 synchronized (mLock) {
2007 mForceAdoptable = (flags & StorageManager.DEBUG_FORCE_ADOPTABLE) != 0;
2008
2009 writeSettingsLocked();
2010 mHandler.obtainMessage(H_RESET).sendToTarget();
2011 }
Jeff Sharkey4c099d02015-05-15 13:45:00 -07002012 }
Jeff Sharkey33dd1562016-04-07 11:05:33 -06002013
2014 if ((mask & (StorageManager.DEBUG_SDCARDFS_FORCE_ON
2015 | StorageManager.DEBUG_SDCARDFS_FORCE_OFF)) != 0) {
2016 final String value;
2017 if ((flags & StorageManager.DEBUG_SDCARDFS_FORCE_ON) != 0) {
2018 value = "force_on";
2019 } else if ((flags & StorageManager.DEBUG_SDCARDFS_FORCE_OFF) != 0) {
2020 value = "force_off";
2021 } else {
2022 value = "";
2023 }
2024
2025 final long token = Binder.clearCallingIdentity();
2026 try {
2027 SystemProperties.set(StorageManager.PROP_SDCARDFS, value);
2028
2029 // Reset storage to kick new setting into place
2030 mHandler.obtainMessage(H_RESET).sendToTarget();
2031 } finally {
2032 Binder.restoreCallingIdentity(token);
2033 }
2034 }
Jeff Sharkey4c099d02015-05-15 13:45:00 -07002035 }
2036
2037 @Override
Jeff Sharkey275e3e42015-04-24 16:10:32 -07002038 public String getPrimaryStorageUuid() {
2039 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
2040 waitForReady();
2041
Jeff Sharkey620b32b2015-04-23 19:36:02 -07002042 synchronized (mLock) {
2043 return mPrimaryStorageUuid;
2044 }
2045 }
2046
2047 @Override
Jeff Sharkey275e3e42015-04-24 16:10:32 -07002048 public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) {
2049 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
2050 waitForReady();
Jeff Sharkey620b32b2015-04-23 19:36:02 -07002051
Jeff Sharkeya31460c2016-06-22 09:04:33 -06002052 final VolumeInfo from;
2053 final VolumeInfo to;
2054
Jeff Sharkey275e3e42015-04-24 16:10:32 -07002055 synchronized (mLock) {
Jeff Sharkeyfced5342015-05-10 14:53:34 -07002056 if (Objects.equals(mPrimaryStorageUuid, volumeUuid)) {
2057 throw new IllegalArgumentException("Primary storage already at " + volumeUuid);
Jeff Sharkey275e3e42015-04-24 16:10:32 -07002058 }
2059
2060 if (mMoveCallback != null) {
2061 throw new IllegalStateException("Move already in progress");
2062 }
2063 mMoveCallback = callback;
2064 mMoveTargetUuid = volumeUuid;
2065
Jeff Sharkeyfced5342015-05-10 14:53:34 -07002066 // When moving to/from primary physical volume, we probably just nuked
2067 // the current storage location, so we have nothing to move.
2068 if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, mPrimaryStorageUuid)
2069 || Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
2070 Slog.d(TAG, "Skipping move to/from primary physical");
2071 onMoveStatusLocked(MOVE_STATUS_COPY_FINISHED);
2072 onMoveStatusLocked(PackageManager.MOVE_SUCCEEDED);
Jeff Sharkey5a9bb742015-11-03 10:15:57 -08002073 mHandler.obtainMessage(H_RESET).sendToTarget();
Jeff Sharkeya31460c2016-06-22 09:04:33 -06002074 return;
Jeff Sharkeyfced5342015-05-10 14:53:34 -07002075
2076 } else {
Jeff Sharkeya31460c2016-06-22 09:04:33 -06002077 from = findStorageForUuid(mPrimaryStorageUuid);
2078 to = findStorageForUuid(volumeUuid);
Jeff Sharkeyef10ee02015-07-05 14:17:27 -07002079
2080 if (from == null) {
2081 Slog.w(TAG, "Failing move due to missing from volume " + mPrimaryStorageUuid);
2082 onMoveStatusLocked(PackageManager.MOVE_FAILED_INTERNAL_ERROR);
2083 return;
2084 } else if (to == null) {
2085 Slog.w(TAG, "Failing move due to missing to volume " + volumeUuid);
2086 onMoveStatusLocked(PackageManager.MOVE_FAILED_INTERNAL_ERROR);
2087 return;
2088 }
Jeff Sharkey275e3e42015-04-24 16:10:32 -07002089 }
Jeff Sharkey620b32b2015-04-23 19:36:02 -07002090 }
Jeff Sharkeya31460c2016-06-22 09:04:33 -06002091
2092 try {
2093 mConnector.execute("volume", "move_storage", from.id, to.id);
2094 } catch (NativeDaemonConnectorException e) {
2095 throw e.rethrowAsParcelableException();
2096 }
Jeff Sharkey620b32b2015-04-23 19:36:02 -07002097 }
2098
2099 @Override
Mike Lockwoodecedfdc2011-06-08 15:11:59 -07002100 public int[] getStorageUsers(String path) {
Jeff Sharkey48877892015-03-18 11:27:19 -07002101 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
San Mehatc1b4ce92010-02-16 17:13:03 -08002102 waitForReady();
2103 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002104 final String[] r = NativeDaemonEvent.filterMessageList(
2105 mConnector.executeForList("storage", "users", path),
2106 VoldResponseCode.StorageUsersListResult);
2107
San Mehatc1b4ce92010-02-16 17:13:03 -08002108 // FMT: <pid> <process name>
2109 int[] data = new int[r.length];
2110 for (int i = 0; i < r.length; i++) {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002111 String[] tok = r[i].split(" ");
San Mehatc1b4ce92010-02-16 17:13:03 -08002112 try {
2113 data[i] = Integer.parseInt(tok[0]);
2114 } catch (NumberFormatException nfe) {
San Mehata5078592010-03-25 09:36:54 -07002115 Slog.e(TAG, String.format("Error parsing pid %s", tok[0]));
San Mehatc1b4ce92010-02-16 17:13:03 -08002116 return new int[0];
2117 }
2118 }
2119 return data;
2120 } catch (NativeDaemonConnectorException e) {
San Mehata5078592010-03-25 09:36:54 -07002121 Slog.e(TAG, "Failed to retrieve storage users list", e);
San Mehatc1b4ce92010-02-16 17:13:03 -08002122 return new int[0];
2123 }
2124 }
2125
San Mehatb1043402010-02-05 08:26:50 -08002126 private void warnOnNotMounted() {
Jeff Sharkey48877892015-03-18 11:27:19 -07002127 synchronized (mLock) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002128 for (int i = 0; i < mVolumes.size(); i++) {
2129 final VolumeInfo vol = mVolumes.valueAt(i);
Jeff Sharkey27de30d2015-04-18 16:20:27 -07002130 if (vol.isPrimary() && vol.isMountedWritable()) {
Jeff Sharkey48877892015-03-18 11:27:19 -07002131 // Cool beans, we have a mounted primary volume
2132 return;
2133 }
Jeff Sharkey32ee8312012-09-30 13:21:31 -07002134 }
San Mehatb1043402010-02-05 08:26:50 -08002135 }
Jeff Sharkey48877892015-03-18 11:27:19 -07002136
2137 Slog.w(TAG, "No primary storage mounted!");
San Mehatb1043402010-02-05 08:26:50 -08002138 }
2139
San Mehat4270e1e2010-01-29 05:32:19 -08002140 public String[] getSecureContainerList() {
Jeff Sharkey48877892015-03-18 11:27:19 -07002141 enforcePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat207e5382010-02-04 20:46:54 -08002142 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08002143 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08002144
San Mehat4270e1e2010-01-29 05:32:19 -08002145 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002146 return NativeDaemonEvent.filterMessageList(
2147 mConnector.executeForList("asec", "list"), VoldResponseCode.AsecListResult);
San Mehat4270e1e2010-01-29 05:32:19 -08002148 } catch (NativeDaemonConnectorException e) {
2149 return new String[0];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002150 }
2151 }
San Mehat36972292010-01-06 11:06:32 -08002152
Kenny Root6dceb882012-04-12 14:23:49 -07002153 public int createSecureContainer(String id, int sizeMb, String fstype, String key,
2154 int ownerUid, boolean external) {
Jeff Sharkey48877892015-03-18 11:27:19 -07002155 enforcePermission(android.Manifest.permission.ASEC_CREATE);
San Mehat207e5382010-02-04 20:46:54 -08002156 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08002157 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08002158
San Mehatb1043402010-02-05 08:26:50 -08002159 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08002160 try {
Jeff Sharkey56cd6462013-06-07 15:09:15 -07002161 mConnector.execute("asec", "create", id, sizeMb, fstype, new SensitiveArg(key),
2162 ownerUid, external ? "1" : "0");
San Mehat4270e1e2010-01-29 05:32:19 -08002163 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08002164 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08002165 }
San Mehata181b212010-02-11 06:50:20 -08002166
2167 if (rc == StorageResultCode.OperationSucceeded) {
2168 synchronized (mAsecMountSet) {
2169 mAsecMountSet.add(id);
2170 }
2171 }
San Mehat4270e1e2010-01-29 05:32:19 -08002172 return rc;
San Mehat36972292010-01-06 11:06:32 -08002173 }
2174
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07002175 @Override
2176 public int resizeSecureContainer(String id, int sizeMb, String key) {
Jeff Sharkey48877892015-03-18 11:27:19 -07002177 enforcePermission(android.Manifest.permission.ASEC_CREATE);
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07002178 waitForReady();
2179 warnOnNotMounted();
2180
2181 int rc = StorageResultCode.OperationSucceeded;
2182 try {
2183 mConnector.execute("asec", "resize", id, sizeMb, new SensitiveArg(key));
2184 } catch (NativeDaemonConnectorException e) {
2185 rc = StorageResultCode.OperationFailedInternalError;
2186 }
2187 return rc;
2188 }
2189
San Mehat4270e1e2010-01-29 05:32:19 -08002190 public int finalizeSecureContainer(String id) {
Jeff Sharkey48877892015-03-18 11:27:19 -07002191 enforcePermission(android.Manifest.permission.ASEC_CREATE);
San Mehatb1043402010-02-05 08:26:50 -08002192 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08002193
San Mehatb1043402010-02-05 08:26:50 -08002194 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08002195 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002196 mConnector.execute("asec", "finalize", id);
San Mehata181b212010-02-11 06:50:20 -08002197 /*
2198 * Finalization does a remount, so no need
2199 * to update mAsecMountSet
2200 */
San Mehat4270e1e2010-01-29 05:32:19 -08002201 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08002202 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08002203 }
San Mehat4270e1e2010-01-29 05:32:19 -08002204 return rc;
San Mehat36972292010-01-06 11:06:32 -08002205 }
2206
Kenny Root6dceb882012-04-12 14:23:49 -07002207 public int fixPermissionsSecureContainer(String id, int gid, String filename) {
Jeff Sharkey48877892015-03-18 11:27:19 -07002208 enforcePermission(android.Manifest.permission.ASEC_CREATE);
Kenny Root6dceb882012-04-12 14:23:49 -07002209 warnOnNotMounted();
2210
2211 int rc = StorageResultCode.OperationSucceeded;
2212 try {
2213 mConnector.execute("asec", "fixperms", id, gid, filename);
2214 /*
2215 * Fix permissions does a remount, so no need to update
2216 * mAsecMountSet
2217 */
2218 } catch (NativeDaemonConnectorException e) {
2219 rc = StorageResultCode.OperationFailedInternalError;
2220 }
2221 return rc;
2222 }
2223
San Mehatd9709982010-02-18 11:43:03 -08002224 public int destroySecureContainer(String id, boolean force) {
Jeff Sharkey48877892015-03-18 11:27:19 -07002225 enforcePermission(android.Manifest.permission.ASEC_DESTROY);
San Mehat207e5382010-02-04 20:46:54 -08002226 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08002227 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08002228
Kenny Rootaa485402010-09-14 14:49:41 -07002229 /*
2230 * Force a GC to make sure AssetManagers in other threads of the
2231 * system_server are cleaned up. We have to do this since AssetManager
2232 * instances are kept as a WeakReference and it's possible we have files
2233 * open on the external storage.
2234 */
2235 Runtime.getRuntime().gc();
2236
San Mehatb1043402010-02-05 08:26:50 -08002237 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08002238 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002239 final Command cmd = new Command("asec", "destroy", id);
2240 if (force) {
2241 cmd.appendArg("force");
2242 }
2243 mConnector.execute(cmd);
San Mehat4270e1e2010-01-29 05:32:19 -08002244 } catch (NativeDaemonConnectorException e) {
San Mehatd9709982010-02-18 11:43:03 -08002245 int code = e.getCode();
2246 if (code == VoldResponseCode.OpFailedStorageBusy) {
2247 rc = StorageResultCode.OperationFailedStorageBusy;
2248 } else {
2249 rc = StorageResultCode.OperationFailedInternalError;
2250 }
San Mehat02735bc2010-01-26 15:18:08 -08002251 }
San Mehata181b212010-02-11 06:50:20 -08002252
2253 if (rc == StorageResultCode.OperationSucceeded) {
2254 synchronized (mAsecMountSet) {
2255 if (mAsecMountSet.contains(id)) {
2256 mAsecMountSet.remove(id);
2257 }
2258 }
2259 }
2260
San Mehat4270e1e2010-01-29 05:32:19 -08002261 return rc;
San Mehat36972292010-01-06 11:06:32 -08002262 }
Jason parks9ed98bc2011-01-17 09:58:35 -06002263
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07002264 public int mountSecureContainer(String id, String key, int ownerUid, boolean readOnly) {
Jeff Sharkey48877892015-03-18 11:27:19 -07002265 enforcePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
San Mehat207e5382010-02-04 20:46:54 -08002266 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08002267 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08002268
San Mehata181b212010-02-11 06:50:20 -08002269 synchronized (mAsecMountSet) {
2270 if (mAsecMountSet.contains(id)) {
2271 return StorageResultCode.OperationFailedStorageMounted;
2272 }
2273 }
2274
San Mehatb1043402010-02-05 08:26:50 -08002275 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08002276 try {
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07002277 mConnector.execute("asec", "mount", id, new SensitiveArg(key), ownerUid,
2278 readOnly ? "ro" : "rw");
San Mehat4270e1e2010-01-29 05:32:19 -08002279 } catch (NativeDaemonConnectorException e) {
Kenny Rootf0304622010-03-19 19:20:42 -07002280 int code = e.getCode();
2281 if (code != VoldResponseCode.OpFailedStorageBusy) {
2282 rc = StorageResultCode.OperationFailedInternalError;
2283 }
San Mehat02735bc2010-01-26 15:18:08 -08002284 }
San Mehat6cdd9c02010-02-09 14:45:20 -08002285
2286 if (rc == StorageResultCode.OperationSucceeded) {
2287 synchronized (mAsecMountSet) {
2288 mAsecMountSet.add(id);
2289 }
2290 }
San Mehat4270e1e2010-01-29 05:32:19 -08002291 return rc;
San Mehat36972292010-01-06 11:06:32 -08002292 }
2293
San Mehatd9709982010-02-18 11:43:03 -08002294 public int unmountSecureContainer(String id, boolean force) {
Jeff Sharkey48877892015-03-18 11:27:19 -07002295 enforcePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
San Mehat207e5382010-02-04 20:46:54 -08002296 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08002297 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08002298
San Mehat6cdd9c02010-02-09 14:45:20 -08002299 synchronized (mAsecMountSet) {
2300 if (!mAsecMountSet.contains(id)) {
San Mehata181b212010-02-11 06:50:20 -08002301 return StorageResultCode.OperationFailedStorageNotMounted;
San Mehat6cdd9c02010-02-09 14:45:20 -08002302 }
2303 }
2304
Kenny Rootaa485402010-09-14 14:49:41 -07002305 /*
2306 * Force a GC to make sure AssetManagers in other threads of the
2307 * system_server are cleaned up. We have to do this since AssetManager
2308 * instances are kept as a WeakReference and it's possible we have files
2309 * open on the external storage.
2310 */
2311 Runtime.getRuntime().gc();
2312
San Mehatb1043402010-02-05 08:26:50 -08002313 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08002314 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002315 final Command cmd = new Command("asec", "unmount", id);
2316 if (force) {
2317 cmd.appendArg("force");
2318 }
2319 mConnector.execute(cmd);
San Mehat4270e1e2010-01-29 05:32:19 -08002320 } catch (NativeDaemonConnectorException e) {
San Mehatd9709982010-02-18 11:43:03 -08002321 int code = e.getCode();
2322 if (code == VoldResponseCode.OpFailedStorageBusy) {
2323 rc = StorageResultCode.OperationFailedStorageBusy;
2324 } else {
2325 rc = StorageResultCode.OperationFailedInternalError;
2326 }
San Mehat02735bc2010-01-26 15:18:08 -08002327 }
San Mehat6cdd9c02010-02-09 14:45:20 -08002328
2329 if (rc == StorageResultCode.OperationSucceeded) {
2330 synchronized (mAsecMountSet) {
2331 mAsecMountSet.remove(id);
2332 }
2333 }
San Mehat4270e1e2010-01-29 05:32:19 -08002334 return rc;
San Mehat9dba7092010-01-18 06:47:41 -08002335 }
2336
San Mehat6cdd9c02010-02-09 14:45:20 -08002337 public boolean isSecureContainerMounted(String id) {
Jeff Sharkey48877892015-03-18 11:27:19 -07002338 enforcePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat6cdd9c02010-02-09 14:45:20 -08002339 waitForReady();
2340 warnOnNotMounted();
2341
2342 synchronized (mAsecMountSet) {
2343 return mAsecMountSet.contains(id);
2344 }
2345 }
2346
San Mehat4270e1e2010-01-29 05:32:19 -08002347 public int renameSecureContainer(String oldId, String newId) {
Jeff Sharkey48877892015-03-18 11:27:19 -07002348 enforcePermission(android.Manifest.permission.ASEC_RENAME);
San Mehat207e5382010-02-04 20:46:54 -08002349 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08002350 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08002351
San Mehata181b212010-02-11 06:50:20 -08002352 synchronized (mAsecMountSet) {
San Mehat85451ee2010-02-24 08:54:18 -08002353 /*
Jason parks9ed98bc2011-01-17 09:58:35 -06002354 * Because a mounted container has active internal state which cannot be
San Mehat85451ee2010-02-24 08:54:18 -08002355 * changed while active, we must ensure both ids are not currently mounted.
2356 */
2357 if (mAsecMountSet.contains(oldId) || mAsecMountSet.contains(newId)) {
San Mehata181b212010-02-11 06:50:20 -08002358 return StorageResultCode.OperationFailedStorageMounted;
2359 }
2360 }
2361
San Mehatb1043402010-02-05 08:26:50 -08002362 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08002363 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002364 mConnector.execute("asec", "rename", oldId, newId);
San Mehat4270e1e2010-01-29 05:32:19 -08002365 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08002366 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08002367 }
San Mehata181b212010-02-11 06:50:20 -08002368
San Mehat4270e1e2010-01-29 05:32:19 -08002369 return rc;
San Mehat45f61042010-01-23 08:12:43 -08002370 }
2371
San Mehat4270e1e2010-01-29 05:32:19 -08002372 public String getSecureContainerPath(String id) {
Jeff Sharkey48877892015-03-18 11:27:19 -07002373 enforcePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat207e5382010-02-04 20:46:54 -08002374 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08002375 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08002376
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002377 final NativeDaemonEvent event;
San Mehat2d66cef2010-03-23 11:12:52 -07002378 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002379 event = mConnector.execute("asec", "path", id);
2380 event.checkCode(VoldResponseCode.AsecPathResult);
2381 return event.getMessage();
San Mehat2d66cef2010-03-23 11:12:52 -07002382 } catch (NativeDaemonConnectorException e) {
2383 int code = e.getCode();
2384 if (code == VoldResponseCode.OpFailedStorageNotFound) {
Fredrik Helmera20c8ef2011-02-09 16:16:10 +01002385 Slog.i(TAG, String.format("Container '%s' not found", id));
2386 return null;
San Mehat22dd86e2010-01-12 12:21:18 -08002387 } else {
San Mehat2d66cef2010-03-23 11:12:52 -07002388 throw new IllegalStateException(String.format("Unexpected response code %d", code));
San Mehat22dd86e2010-01-12 12:21:18 -08002389 }
2390 }
San Mehat22dd86e2010-01-12 12:21:18 -08002391 }
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -07002392
Dianne Hackborn292f8bc2011-06-27 16:27:41 -07002393 public String getSecureContainerFilesystemPath(String id) {
Jeff Sharkey48877892015-03-18 11:27:19 -07002394 enforcePermission(android.Manifest.permission.ASEC_ACCESS);
Dianne Hackborn292f8bc2011-06-27 16:27:41 -07002395 waitForReady();
2396 warnOnNotMounted();
2397
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002398 final NativeDaemonEvent event;
Dianne Hackborn292f8bc2011-06-27 16:27:41 -07002399 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002400 event = mConnector.execute("asec", "fspath", id);
2401 event.checkCode(VoldResponseCode.AsecPathResult);
2402 return event.getMessage();
Dianne Hackborn292f8bc2011-06-27 16:27:41 -07002403 } catch (NativeDaemonConnectorException e) {
2404 int code = e.getCode();
2405 if (code == VoldResponseCode.OpFailedStorageNotFound) {
2406 Slog.i(TAG, String.format("Container '%s' not found", id));
2407 return null;
2408 } else {
2409 throw new IllegalStateException(String.format("Unexpected response code %d", code));
2410 }
2411 }
2412 }
2413
Jeff Sharkey48877892015-03-18 11:27:19 -07002414 @Override
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -07002415 public void finishMediaUpdate() {
Jeff Sharkey9527b222015-06-24 15:24:48 -07002416 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
Rubin Xucd7a0142015-04-17 23:45:27 +01002417 throw new SecurityException("no permission to call finishMediaUpdate()");
2418 }
Jeff Sharkey48877892015-03-18 11:27:19 -07002419 if (mUnmountSignal != null) {
2420 mUnmountSignal.countDown();
2421 } else {
2422 Slog.w(TAG, "Odd, nobody asked to unmount?");
2423 }
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -07002424 }
Kenny Root02c87302010-07-01 08:10:18 -07002425
Kenny Roota02b8b02010-08-05 16:14:17 -07002426 private boolean isUidOwnerOfPackageOrSystem(String packageName, int callerUid) {
2427 if (callerUid == android.os.Process.SYSTEM_UID) {
2428 return true;
2429 }
2430
Kenny Root02c87302010-07-01 08:10:18 -07002431 if (packageName == null) {
2432 return false;
2433 }
2434
Jeff Sharkeycd654482016-01-08 17:42:11 -07002435 final int packageUid = mPms.getPackageUid(packageName,
2436 PackageManager.MATCH_DEBUG_TRIAGED_MISSING, UserHandle.getUserId(callerUid));
Kenny Root02c87302010-07-01 08:10:18 -07002437
2438 if (DEBUG_OBB) {
2439 Slog.d(TAG, "packageName = " + packageName + ", packageUid = " +
2440 packageUid + ", callerUid = " + callerUid);
2441 }
2442
2443 return callerUid == packageUid;
2444 }
2445
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002446 public String getMountedObbPath(String rawPath) {
2447 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
Kenny Rootaf9d6672010-10-08 09:21:39 -07002448
Kenny Root02c87302010-07-01 08:10:18 -07002449 waitForReady();
2450 warnOnNotMounted();
2451
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002452 final ObbState state;
Rubin Xucd7a0142015-04-17 23:45:27 +01002453 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002454 state = mObbPathToStateMap.get(rawPath);
2455 }
2456 if (state == null) {
2457 Slog.w(TAG, "Failed to find OBB mounted at " + rawPath);
2458 return null;
2459 }
2460
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002461 final NativeDaemonEvent event;
Kenny Root02c87302010-07-01 08:10:18 -07002462 try {
Xiaohui Chen621b3fc2015-10-02 14:41:42 -07002463 event = mConnector.execute("obb", "path", state.canonicalPath);
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002464 event.checkCode(VoldResponseCode.AsecPathResult);
2465 return event.getMessage();
Kenny Root02c87302010-07-01 08:10:18 -07002466 } catch (NativeDaemonConnectorException e) {
2467 int code = e.getCode();
2468 if (code == VoldResponseCode.OpFailedStorageNotFound) {
Kenny Roota02b8b02010-08-05 16:14:17 -07002469 return null;
Kenny Root02c87302010-07-01 08:10:18 -07002470 } else {
2471 throw new IllegalStateException(String.format("Unexpected response code %d", code));
2472 }
2473 }
2474 }
2475
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002476 @Override
2477 public boolean isObbMounted(String rawPath) {
2478 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
Kenny Rootaf9d6672010-10-08 09:21:39 -07002479 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002480 return mObbPathToStateMap.containsKey(rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002481 }
Kenny Root02c87302010-07-01 08:10:18 -07002482 }
2483
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002484 @Override
2485 public void mountObb(
2486 String rawPath, String canonicalPath, String key, IObbActionListener token, int nonce) {
2487 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
2488 Preconditions.checkNotNull(canonicalPath, "canonicalPath cannot be null");
2489 Preconditions.checkNotNull(token, "token cannot be null");
Kenny Rootaf9d6672010-10-08 09:21:39 -07002490
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002491 final int callingUid = Binder.getCallingUid();
2492 final ObbState obbState = new ObbState(rawPath, canonicalPath, callingUid, token, nonce);
2493 final ObbAction action = new MountObbAction(obbState, key, callingUid);
Kenny Roota02b8b02010-08-05 16:14:17 -07002494 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
2495
2496 if (DEBUG_OBB)
2497 Slog.i(TAG, "Send to OBB handler: " + action.toString());
Kenny Root02c87302010-07-01 08:10:18 -07002498 }
2499
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002500 @Override
2501 public void unmountObb(String rawPath, boolean force, IObbActionListener token, int nonce) {
2502 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
2503
2504 final ObbState existingState;
Rubin Xucd7a0142015-04-17 23:45:27 +01002505 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002506 existingState = mObbPathToStateMap.get(rawPath);
Kenny Rootf1121dc2010-09-29 07:30:53 -07002507 }
2508
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002509 if (existingState != null) {
2510 // TODO: separate state object from request data
2511 final int callingUid = Binder.getCallingUid();
2512 final ObbState newState = new ObbState(
2513 rawPath, existingState.canonicalPath, callingUid, token, nonce);
2514 final ObbAction action = new UnmountObbAction(newState, force);
2515 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
Kenny Root02c87302010-07-01 08:10:18 -07002516
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002517 if (DEBUG_OBB)
2518 Slog.i(TAG, "Send to OBB handler: " + action.toString());
2519 } else {
2520 Slog.w(TAG, "Unknown OBB mount at " + rawPath);
2521 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002522 }
2523
Ben Komalo444eca22011-09-01 15:17:44 -07002524 @Override
2525 public int getEncryptionState() {
2526 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2527 "no permission to access the crypt keeper");
2528
2529 waitForReady();
2530
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002531 final NativeDaemonEvent event;
Ben Komalo444eca22011-09-01 15:17:44 -07002532 try {
Paul Lawrence1c62cbb2015-06-03 14:14:52 -07002533 event = mCryptConnector.execute("cryptfs", "cryptocomplete");
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002534 return Integer.parseInt(event.getMessage());
Ben Komalo444eca22011-09-01 15:17:44 -07002535 } catch (NumberFormatException e) {
2536 // Bad result - unexpected.
2537 Slog.w(TAG, "Unable to parse result from cryptfs cryptocomplete");
2538 return ENCRYPTION_STATE_ERROR_UNKNOWN;
2539 } catch (NativeDaemonConnectorException e) {
2540 // Something bad happened.
2541 Slog.w(TAG, "Error in communicating with cryptfs in validating");
2542 return ENCRYPTION_STATE_ERROR_UNKNOWN;
2543 }
2544 }
2545
2546 @Override
Jason parks5af0b912010-11-29 09:05:25 -06002547 public int decryptStorage(String password) {
Jason parksf7b3cd42011-01-27 09:28:25 -06002548 if (TextUtils.isEmpty(password)) {
2549 throw new IllegalArgumentException("password cannot be empty");
Jason parks5af0b912010-11-29 09:05:25 -06002550 }
2551
Jason parks8888c592011-01-20 22:46:41 -06002552 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2553 "no permission to access the crypt keeper");
Jason parks5af0b912010-11-29 09:05:25 -06002554
2555 waitForReady();
2556
2557 if (DEBUG_EVENTS) {
2558 Slog.i(TAG, "decrypting storage...");
2559 }
2560
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002561 final NativeDaemonEvent event;
Jason parks5af0b912010-11-29 09:05:25 -06002562 try {
Paul Lawrence05487612015-06-09 13:35:38 -07002563 event = mCryptConnector.execute("cryptfs", "checkpw", new SensitiveArg(password));
Jason parks9ed98bc2011-01-17 09:58:35 -06002564
Fredrik Roubertda6aedf2011-12-20 17:34:43 +01002565 final int code = Integer.parseInt(event.getMessage());
Jason parks9ed98bc2011-01-17 09:58:35 -06002566 if (code == 0) {
2567 // Decrypt was successful. Post a delayed message before restarting in order
2568 // to let the UI to clear itself
2569 mHandler.postDelayed(new Runnable() {
2570 public void run() {
Jeff Sharkey31c6e482011-11-18 17:09:01 -08002571 try {
Paul Lawrence1c62cbb2015-06-03 14:14:52 -07002572 mCryptConnector.execute("cryptfs", "restart");
Jeff Sharkey31c6e482011-11-18 17:09:01 -08002573 } catch (NativeDaemonConnectorException e) {
2574 Slog.e(TAG, "problem executing in background", e);
2575 }
Jason parks9ed98bc2011-01-17 09:58:35 -06002576 }
Jason parksf7b3cd42011-01-27 09:28:25 -06002577 }, 1000); // 1 second
Jason parks9ed98bc2011-01-17 09:58:35 -06002578 }
2579
2580 return code;
Jason parks5af0b912010-11-29 09:05:25 -06002581 } catch (NativeDaemonConnectorException e) {
2582 // Decryption failed
2583 return e.getCode();
2584 }
Jason parks5af0b912010-11-29 09:05:25 -06002585 }
2586
Paul Lawrence46791e72014-04-03 09:10:26 -07002587 public int encryptStorage(int type, String password) {
2588 if (TextUtils.isEmpty(password) && type != StorageManager.CRYPT_TYPE_DEFAULT) {
Jason parksf7b3cd42011-01-27 09:28:25 -06002589 throw new IllegalArgumentException("password cannot be empty");
Jason parks56aa5322011-01-07 09:01:15 -06002590 }
2591
Jason parks8888c592011-01-20 22:46:41 -06002592 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2593 "no permission to access the crypt keeper");
Jason parks56aa5322011-01-07 09:01:15 -06002594
2595 waitForReady();
2596
2597 if (DEBUG_EVENTS) {
Jason parks8888c592011-01-20 22:46:41 -06002598 Slog.i(TAG, "encrypting storage...");
Jason parks56aa5322011-01-07 09:01:15 -06002599 }
2600
2601 try {
Paul Lawrence5096d9e2015-09-09 13:05:45 -07002602 if (type == StorageManager.CRYPT_TYPE_DEFAULT) {
2603 mCryptConnector.execute("cryptfs", "enablecrypto", "inplace",
2604 CRYPTO_TYPES[type]);
2605 } else {
2606 mCryptConnector.execute("cryptfs", "enablecrypto", "inplace",
2607 CRYPTO_TYPES[type], new SensitiveArg(password));
2608 }
Jason parks56aa5322011-01-07 09:01:15 -06002609 } catch (NativeDaemonConnectorException e) {
2610 // Encryption failed
2611 return e.getCode();
2612 }
2613
2614 return 0;
2615 }
2616
Paul Lawrence8e397362014-01-27 15:22:30 -08002617 /** Set the password for encrypting the master key.
2618 * @param type One of the CRYPTO_TYPE_XXX consts defined in StorageManager.
2619 * @param password The password to set.
2620 */
2621 public int changeEncryptionPassword(int type, String password) {
Jason parksf7b3cd42011-01-27 09:28:25 -06002622 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2623 "no permission to access the crypt keeper");
2624
2625 waitForReady();
2626
2627 if (DEBUG_EVENTS) {
2628 Slog.i(TAG, "changing encryption password...");
2629 }
2630
2631 try {
Paul Lawrence1c62cbb2015-06-03 14:14:52 -07002632 NativeDaemonEvent event = mCryptConnector.execute("cryptfs", "changepw", CRYPTO_TYPES[type],
Paul Lawrence05487612015-06-09 13:35:38 -07002633 new SensitiveArg(password));
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002634 return Integer.parseInt(event.getMessage());
Jason parksf7b3cd42011-01-27 09:28:25 -06002635 } catch (NativeDaemonConnectorException e) {
2636 // Encryption failed
2637 return e.getCode();
2638 }
2639 }
2640
Christopher Tate32418be2011-10-10 13:51:12 -07002641 /**
2642 * Validate a user-supplied password string with cryptfs
2643 */
2644 @Override
2645 public int verifyEncryptionPassword(String password) throws RemoteException {
2646 // Only the system process is permitted to validate passwords
2647 if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
2648 throw new SecurityException("no permission to access the crypt keeper");
2649 }
2650
2651 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2652 "no permission to access the crypt keeper");
2653
2654 if (TextUtils.isEmpty(password)) {
2655 throw new IllegalArgumentException("password cannot be empty");
2656 }
2657
2658 waitForReady();
2659
2660 if (DEBUG_EVENTS) {
2661 Slog.i(TAG, "validating encryption password...");
2662 }
2663
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002664 final NativeDaemonEvent event;
Christopher Tate32418be2011-10-10 13:51:12 -07002665 try {
Paul Lawrence05487612015-06-09 13:35:38 -07002666 event = mCryptConnector.execute("cryptfs", "verifypw", new SensitiveArg(password));
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002667 Slog.i(TAG, "cryptfs verifypw => " + event.getMessage());
2668 return Integer.parseInt(event.getMessage());
Christopher Tate32418be2011-10-10 13:51:12 -07002669 } catch (NativeDaemonConnectorException e) {
2670 // Encryption failed
2671 return e.getCode();
2672 }
2673 }
2674
Paul Lawrence8e397362014-01-27 15:22:30 -08002675 /**
2676 * Get the type of encryption used to encrypt the master key.
2677 * @return The type, one of the CRYPT_TYPE_XXX consts from StorageManager.
2678 */
2679 @Override
Svetoslav16e4a1a2014-09-29 18:16:20 -07002680 public int getPasswordType() {
Paul Lawrence9de713d2016-05-02 22:45:33 +00002681 mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL,
2682 "no permission to access the crypt keeper");
2683
Paul Lawrence8e397362014-01-27 15:22:30 -08002684 waitForReady();
2685
2686 final NativeDaemonEvent event;
2687 try {
Paul Lawrence1c62cbb2015-06-03 14:14:52 -07002688 event = mCryptConnector.execute("cryptfs", "getpwtype");
Paul Lawrence8e397362014-01-27 15:22:30 -08002689 for (int i = 0; i < CRYPTO_TYPES.length; ++i) {
2690 if (CRYPTO_TYPES[i].equals(event.getMessage()))
2691 return i;
2692 }
2693
2694 throw new IllegalStateException("unexpected return from cryptfs");
2695 } catch (NativeDaemonConnectorException e) {
2696 throw e.rethrowAsParcelableException();
2697 }
2698 }
2699
Paul Lawrencee51dcf92014-03-18 10:56:00 -07002700 /**
2701 * Set a field in the crypto header.
2702 * @param field field to set
2703 * @param contents contents to set in field
2704 */
2705 @Override
2706 public void setField(String field, String contents) throws RemoteException {
Paul Lawrence0bbd1082016-04-26 15:21:02 -07002707 mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL,
2708 "no permission to access the crypt keeper");
Paul Lawrencee51dcf92014-03-18 10:56:00 -07002709
2710 waitForReady();
2711
2712 final NativeDaemonEvent event;
2713 try {
Paul Lawrence1c62cbb2015-06-03 14:14:52 -07002714 event = mCryptConnector.execute("cryptfs", "setfield", field, contents);
Paul Lawrencee51dcf92014-03-18 10:56:00 -07002715 } catch (NativeDaemonConnectorException e) {
2716 throw e.rethrowAsParcelableException();
2717 }
2718 }
2719
2720 /**
2721 * Gets a field from the crypto header.
2722 * @param field field to get
2723 * @return contents of field
2724 */
2725 @Override
2726 public String getField(String field) throws RemoteException {
Paul Lawrence0bbd1082016-04-26 15:21:02 -07002727 mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL,
2728 "no permission to access the crypt keeper");
Paul Lawrencee51dcf92014-03-18 10:56:00 -07002729
2730 waitForReady();
2731
2732 final NativeDaemonEvent event;
2733 try {
2734 final String[] contents = NativeDaemonEvent.filterMessageList(
Paul Lawrence1c62cbb2015-06-03 14:14:52 -07002735 mCryptConnector.executeForList("cryptfs", "getfield", field),
Paul Lawrencee51dcf92014-03-18 10:56:00 -07002736 VoldResponseCode.CryptfsGetfieldResult);
2737 String result = new String();
2738 for (String content : contents) {
2739 result += content;
2740 }
2741 return result;
2742 } catch (NativeDaemonConnectorException e) {
2743 throw e.rethrowAsParcelableException();
2744 }
2745 }
2746
Paul Lawrence3806d9c2015-10-29 10:30:46 -07002747 /**
2748 * Is userdata convertible to file based encryption?
2749 * @return non zero for convertible
2750 */
2751 @Override
2752 public boolean isConvertibleToFBE() throws RemoteException {
Paul Lawrence0bbd1082016-04-26 15:21:02 -07002753 mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL,
2754 "no permission to access the crypt keeper");
Paul Lawrence3806d9c2015-10-29 10:30:46 -07002755
2756 waitForReady();
2757
2758 final NativeDaemonEvent event;
2759 try {
2760 event = mCryptConnector.execute("cryptfs", "isConvertibleToFBE");
2761 return Integer.parseInt(event.getMessage()) != 0;
2762 } catch (NativeDaemonConnectorException e) {
2763 throw e.rethrowAsParcelableException();
2764 }
2765 }
2766
Jeff Sharkeyb049e212012-09-07 23:16:01 -07002767 @Override
Paul Lawrence945490c2014-03-27 16:37:28 +00002768 public String getPassword() throws RemoteException {
Paul Lawrence0bbd1082016-04-26 15:21:02 -07002769 mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL,
Rubin Xucd7a0142015-04-17 23:45:27 +01002770 "only keyguard can retrieve password");
Paul Lawrence0bbd1082016-04-26 15:21:02 -07002771
Paul Lawrence945490c2014-03-27 16:37:28 +00002772 if (!isReady()) {
2773 return new String();
2774 }
2775
2776 final NativeDaemonEvent event;
2777 try {
Paul Lawrence1c62cbb2015-06-03 14:14:52 -07002778 event = mCryptConnector.execute("cryptfs", "getpw");
Paul Lawrence24063b52015-01-06 13:11:23 -08002779 if ("-1".equals(event.getMessage())) {
2780 // -1 equals no password
2781 return null;
2782 }
Paul Lawrence05487612015-06-09 13:35:38 -07002783 return event.getMessage();
Paul Lawrence945490c2014-03-27 16:37:28 +00002784 } catch (NativeDaemonConnectorException e) {
2785 throw e.rethrowAsParcelableException();
Paul Lawrence24063b52015-01-06 13:11:23 -08002786 } catch (IllegalArgumentException e) {
2787 Slog.e(TAG, "Invalid response to getPassword");
2788 return null;
Paul Lawrence945490c2014-03-27 16:37:28 +00002789 }
2790 }
2791
2792 @Override
2793 public void clearPassword() throws RemoteException {
Paul Lawrence0bbd1082016-04-26 15:21:02 -07002794 mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL,
2795 "only keyguard can clear password");
2796
Paul Lawrence945490c2014-03-27 16:37:28 +00002797 if (!isReady()) {
2798 return;
2799 }
2800
2801 final NativeDaemonEvent event;
2802 try {
Paul Lawrence1c62cbb2015-06-03 14:14:52 -07002803 event = mCryptConnector.execute("cryptfs", "clearpw");
Paul Lawrence945490c2014-03-27 16:37:28 +00002804 } catch (NativeDaemonConnectorException e) {
2805 throw e.rethrowAsParcelableException();
2806 }
2807 }
2808
2809 @Override
Lenka Trochtovac4dd0212015-11-18 12:22:06 +01002810 public void createUserKey(int userId, int serialNumber, boolean ephemeral) {
Jeff Sharkeyf9fc6d62015-11-08 16:46:05 -08002811 enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
Paul Crowleybcf48ed2015-04-22 13:36:59 +01002812 waitForReady();
2813
Paul Crowleybcf48ed2015-04-22 13:36:59 +01002814 try {
Lenka Trochtovac4dd0212015-11-18 12:22:06 +01002815 mCryptConnector.execute("cryptfs", "create_user_key", userId, serialNumber,
2816 ephemeral ? 1 : 0);
Paul Crowleybcf48ed2015-04-22 13:36:59 +01002817 } catch (NativeDaemonConnectorException e) {
Jeff Sharkeyf9fc6d62015-11-08 16:46:05 -08002818 throw e.rethrowAsParcelableException();
Paul Crowleybcf48ed2015-04-22 13:36:59 +01002819 }
2820 }
2821
Paul Crowley7ec733f2015-05-19 12:42:00 +01002822 @Override
Jeff Sharkeyf9fc6d62015-11-08 16:46:05 -08002823 public void destroyUserKey(int userId) {
2824 enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
Paul Crowley7ec733f2015-05-19 12:42:00 +01002825 waitForReady();
2826
Jeff Sharkeyf9fc6d62015-11-08 16:46:05 -08002827 try {
2828 mCryptConnector.execute("cryptfs", "destroy_user_key", userId);
2829 } catch (NativeDaemonConnectorException e) {
2830 throw e.rethrowAsParcelableException();
2831 }
2832 }
2833
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +00002834 private SensitiveArg encodeBytes(byte[] bytes) {
2835 if (ArrayUtils.isEmpty(bytes)) {
2836 return new SensitiveArg("!");
2837 } else {
2838 return new SensitiveArg(HexDump.toHexString(bytes));
2839 }
2840 }
2841
Paul Crowleycc701552016-05-17 14:18:49 -07002842 /*
2843 * Add this token/secret pair to the set of ways we can recover a disk encryption key.
2844 * Changing the token/secret for a disk encryption key is done in two phases: first, adding
2845 * a new token/secret pair with this call, then delting all other pairs with
2846 * fixateNewestUserKeyAuth. This allows other places where a credential is used, such as
2847 * Gatekeeper, to be updated between the two calls.
2848 */
Jeff Sharkeyf9fc6d62015-11-08 16:46:05 -08002849 @Override
Paul Crowleycc701552016-05-17 14:18:49 -07002850 public void addUserKeyAuth(int userId, int serialNumber, byte[] token, byte[] secret) {
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +00002851 enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
2852 waitForReady();
2853
2854 try {
Paul Crowleycc701552016-05-17 14:18:49 -07002855 mCryptConnector.execute("cryptfs", "add_user_key_auth", userId, serialNumber,
2856 encodeBytes(token), encodeBytes(secret));
2857 } catch (NativeDaemonConnectorException e) {
2858 throw e.rethrowAsParcelableException();
2859 }
2860 }
2861
2862 /*
2863 * Delete all disk encryption token/secret pairs except the most recently added one
2864 */
2865 @Override
2866 public void fixateNewestUserKeyAuth(int userId) {
2867 enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
2868 waitForReady();
2869
2870 try {
2871 mCryptConnector.execute("cryptfs", "fixate_newest_user_key_auth", userId);
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +00002872 } catch (NativeDaemonConnectorException e) {
2873 throw e.rethrowAsParcelableException();
2874 }
2875 }
2876
2877 @Override
2878 public void unlockUserKey(int userId, int serialNumber, byte[] token, byte[] secret) {
Jeff Sharkeyf9fc6d62015-11-08 16:46:05 -08002879 enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
2880 waitForReady();
2881
Jeff Sharkeyce18c812016-04-27 16:00:41 -06002882 if (StorageManager.isFileEncryptedNativeOrEmulated()) {
2883 // When a user has secure lock screen, require a challenge token to
2884 // actually unlock. This check is mostly in place for emulation mode.
2885 if (mLockPatternUtils.isSecure(userId) && ArrayUtils.isEmpty(token)) {
2886 throw new IllegalStateException("Token required to unlock secure user " + userId);
2887 }
Jeff Sharkeyb9fe5372015-12-03 15:23:08 -07002888
Jeff Sharkeyce18c812016-04-27 16:00:41 -06002889 try {
2890 mCryptConnector.execute("cryptfs", "unlock_user_key", userId, serialNumber,
2891 encodeBytes(token), encodeBytes(secret));
2892 } catch (NativeDaemonConnectorException e) {
2893 throw e.rethrowAsParcelableException();
2894 }
Jeff Sharkeyf9fc6d62015-11-08 16:46:05 -08002895 }
2896
2897 synchronized (mLock) {
Jeff Sharkeyce14cd02015-12-07 15:35:42 -07002898 mLocalUnlockedUsers = ArrayUtils.appendInt(mLocalUnlockedUsers, userId);
Jeff Sharkeyf9fc6d62015-11-08 16:46:05 -08002899 }
2900 }
2901
2902 @Override
2903 public void lockUserKey(int userId) {
2904 enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
2905 waitForReady();
2906
2907 try {
2908 mCryptConnector.execute("cryptfs", "lock_user_key", userId);
2909 } catch (NativeDaemonConnectorException e) {
2910 throw e.rethrowAsParcelableException();
2911 }
2912
2913 synchronized (mLock) {
Jeff Sharkeyce14cd02015-12-07 15:35:42 -07002914 mLocalUnlockedUsers = ArrayUtils.removeInt(mLocalUnlockedUsers, userId);
Jeff Sharkeyf9fc6d62015-11-08 16:46:05 -08002915 }
2916 }
2917
2918 @Override
2919 public boolean isUserKeyUnlocked(int userId) {
Jeff Sharkeyce18c812016-04-27 16:00:41 -06002920 synchronized (mLock) {
2921 return ArrayUtils.contains(mLocalUnlockedUsers, userId);
Jeff Sharkeyf9fc6d62015-11-08 16:46:05 -08002922 }
2923 }
2924
2925 @Override
Jeff Sharkey47f71082016-02-01 17:03:54 -07002926 public void prepareUserStorage(String volumeUuid, int userId, int serialNumber, int flags) {
Jeff Sharkeyf9fc6d62015-11-08 16:46:05 -08002927 enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
2928 waitForReady();
2929
2930 try {
2931 mCryptConnector.execute("cryptfs", "prepare_user_storage", escapeNull(volumeUuid),
Jeff Sharkey47f71082016-02-01 17:03:54 -07002932 userId, serialNumber, flags);
Jeff Sharkeyf9fc6d62015-11-08 16:46:05 -08002933 } catch (NativeDaemonConnectorException e) {
2934 throw e.rethrowAsParcelableException();
Paul Crowley7ec733f2015-05-19 12:42:00 +01002935 }
2936 }
2937
Paul Crowleybcf48ed2015-04-22 13:36:59 +01002938 @Override
Jeff Sharkeyfcf1e552016-04-14 20:44:58 -06002939 public void destroyUserStorage(String volumeUuid, int userId, int flags) {
2940 enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
2941 waitForReady();
2942
2943 try {
2944 mCryptConnector.execute("cryptfs", "destroy_user_storage", escapeNull(volumeUuid),
2945 userId, flags);
2946 } catch (NativeDaemonConnectorException e) {
2947 throw e.rethrowAsParcelableException();
2948 }
2949 }
2950
2951 @Override
Daichi Hirono91e3b502015-12-16 09:24:16 +09002952 public ParcelFileDescriptor mountAppFuse(final String name) throws RemoteException {
Daichi Hironobee50c02015-12-14 11:00:54 +09002953 try {
Daichi Hirono91e3b502015-12-16 09:24:16 +09002954 final int uid = Binder.getCallingUid();
Daichi Hironofd7d57e2016-01-29 14:30:58 +09002955 final int pid = Binder.getCallingPid();
Daichi Hironobee50c02015-12-14 11:00:54 +09002956 final NativeDaemonEvent event =
Daichi Hironofd7d57e2016-01-29 14:30:58 +09002957 mConnector.execute("appfuse", "mount", uid, pid, name);
Daichi Hironobee50c02015-12-14 11:00:54 +09002958 if (event.getFileDescriptors() == null) {
Daichi Hirono91e3b502015-12-16 09:24:16 +09002959 throw new RemoteException("AppFuse FD from vold is null.");
Daichi Hironobee50c02015-12-14 11:00:54 +09002960 }
Daichi Hirono91e3b502015-12-16 09:24:16 +09002961 return ParcelFileDescriptor.fromFd(
2962 event.getFileDescriptors()[0],
2963 mHandler,
2964 new ParcelFileDescriptor.OnCloseListener() {
2965 @Override
2966 public void onClose(IOException e) {
2967 try {
2968 final NativeDaemonEvent event = mConnector.execute(
Daichi Hironofd7d57e2016-01-29 14:30:58 +09002969 "appfuse", "unmount", uid, pid, name);
Daichi Hirono91e3b502015-12-16 09:24:16 +09002970 } catch (NativeDaemonConnectorException unmountException) {
2971 Log.e(TAG, "Failed to unmount appfuse.");
2972 }
2973 }
2974 });
Daichi Hironobee50c02015-12-14 11:00:54 +09002975 } catch (NativeDaemonConnectorException e) {
2976 throw e.rethrowAsParcelableException();
Daichi Hirono91e3b502015-12-16 09:24:16 +09002977 } catch (IOException e) {
2978 throw new RemoteException(e.getMessage());
Daichi Hironobee50c02015-12-14 11:00:54 +09002979 }
Daichi Hirono9e8d9e22015-11-13 14:37:00 +09002980 }
2981
2982 @Override
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002983 public int mkdirs(String callingPkg, String appPath) {
2984 final int userId = UserHandle.getUserId(Binder.getCallingUid());
2985 final UserEnvironment userEnv = new UserEnvironment(userId);
2986
2987 // Validate that reported package name belongs to caller
2988 final AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(
2989 Context.APP_OPS_SERVICE);
2990 appOps.checkPackage(Binder.getCallingUid(), callingPkg);
2991
Jeff Sharkey48877892015-03-18 11:27:19 -07002992 File appFile = null;
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002993 try {
Jeff Sharkey48877892015-03-18 11:27:19 -07002994 appFile = new File(appPath).getCanonicalFile();
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002995 } catch (IOException e) {
2996 Slog.e(TAG, "Failed to resolve " + appPath + ": " + e);
2997 return -1;
2998 }
2999
3000 // Try translating the app path into a vold path, but require that it
3001 // belong to the calling package.
Jeff Sharkey48877892015-03-18 11:27:19 -07003002 if (FileUtils.contains(userEnv.buildExternalStorageAppDataDirs(callingPkg), appFile) ||
3003 FileUtils.contains(userEnv.buildExternalStorageAppObbDirs(callingPkg), appFile) ||
3004 FileUtils.contains(userEnv.buildExternalStorageAppMediaDirs(callingPkg), appFile)) {
3005 appPath = appFile.getAbsolutePath();
3006 if (!appPath.endsWith("/")) {
3007 appPath = appPath + "/";
3008 }
3009
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07003010 try {
Jeff Sharkey48877892015-03-18 11:27:19 -07003011 mConnector.execute("volume", "mkdirs", appPath);
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07003012 return 0;
3013 } catch (NativeDaemonConnectorException e) {
3014 return e.getCode();
3015 }
3016 }
3017
Jeff Sharkey48877892015-03-18 11:27:19 -07003018 throw new SecurityException("Invalid mkdirs path: " + appFile);
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07003019 }
3020
3021 @Override
Jeff Sharkey46349872015-07-28 10:49:47 -07003022 public StorageVolume[] getVolumeList(int uid, String packageName, int flags) {
Jeff Sharkeyce14cd02015-12-07 15:35:42 -07003023 final int userId = UserHandle.getUserId(uid);
Jeff Sharkeyc02bfae2016-03-27 15:06:53 -06003024
Jeff Sharkey46349872015-07-28 10:49:47 -07003025 final boolean forWrite = (flags & StorageManager.FLAG_FOR_WRITE) != 0;
Jeff Sharkeyc02bfae2016-03-27 15:06:53 -06003026 final boolean realState = (flags & StorageManager.FLAG_REAL_STATE) != 0;
3027 final boolean includeInvisible = (flags & StorageManager.FLAG_INCLUDE_INVISIBLE) != 0;
Jeff Sharkey46349872015-07-28 10:49:47 -07003028
Jeff Sharkeyc02bfae2016-03-27 15:06:53 -06003029 final boolean userKeyUnlocked;
3030 final boolean storagePermission;
3031 final long token = Binder.clearCallingIdentity();
Svetoslav38c3dbb2015-07-14 11:27:06 -07003032 try {
Jeff Sharkeyc02bfae2016-03-27 15:06:53 -06003033 userKeyUnlocked = isUserKeyUnlocked(userId);
3034 storagePermission = mMountServiceInternal.hasExternalStorage(uid, packageName);
Svetoslav38c3dbb2015-07-14 11:27:06 -07003035 } finally {
Jeff Sharkeyc02bfae2016-03-27 15:06:53 -06003036 Binder.restoreCallingIdentity(token);
Svetoslav38c3dbb2015-07-14 11:27:06 -07003037 }
Svet Ganov6ee871e2015-07-10 14:29:33 -07003038
Jeff Sharkeyc02bfae2016-03-27 15:06:53 -06003039 boolean foundPrimary = false;
3040
Jeff Sharkeyce14cd02015-12-07 15:35:42 -07003041 final ArrayList<StorageVolume> res = new ArrayList<>();
Jeff Sharkey48877892015-03-18 11:27:19 -07003042 synchronized (mLock) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07003043 for (int i = 0; i < mVolumes.size(); i++) {
3044 final VolumeInfo vol = mVolumes.valueAt(i);
Jeff Sharkeyc02bfae2016-03-27 15:06:53 -06003045 switch (vol.getType()) {
3046 case VolumeInfo.TYPE_PUBLIC:
3047 case VolumeInfo.TYPE_EMULATED:
3048 break;
3049 default:
3050 continue;
3051 }
3052
3053 boolean match = false;
3054 if (forWrite) {
3055 match = vol.isVisibleForWrite(userId);
3056 } else {
Felipe Leme123a0e72016-06-10 11:09:11 -07003057 match = vol.isVisibleForRead(userId)
3058 || (includeInvisible && vol.getPath() != null);
Jeff Sharkeyc02bfae2016-03-27 15:06:53 -06003059 }
3060 if (!match) continue;
3061
3062 boolean reportUnmounted = false;
3063 if ((vol.getType() == VolumeInfo.TYPE_EMULATED) && !userKeyUnlocked) {
3064 reportUnmounted = true;
3065 } else if (!storagePermission && !realState) {
3066 reportUnmounted = true;
3067 }
3068
3069 final StorageVolume userVol = vol.buildStorageVolume(mContext, userId,
3070 reportUnmounted);
3071 if (vol.isPrimary()) {
3072 res.add(0, userVol);
3073 foundPrimary = true;
3074 } else {
3075 res.add(userVol);
Jeff Sharkeyb049e212012-09-07 23:16:01 -07003076 }
Mike Lockwood8fa5f802011-03-24 08:12:30 -07003077 }
Mike Lockwood8fa5f802011-03-24 08:12:30 -07003078 }
Jeff Sharkey48877892015-03-18 11:27:19 -07003079
3080 if (!foundPrimary) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07003081 Log.w(TAG, "No primary storage defined yet; hacking together a stub");
Jeff Sharkey48877892015-03-18 11:27:19 -07003082
3083 final boolean primaryPhysical = SystemProperties.getBoolean(
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07003084 StorageManager.PROP_PRIMARY_PHYSICAL, false);
Jeff Sharkey48877892015-03-18 11:27:19 -07003085
3086 final String id = "stub_primary";
3087 final File path = Environment.getLegacyExternalStorageDirectory();
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07003088 final String description = mContext.getString(android.R.string.unknownName);
Jeff Sharkey48877892015-03-18 11:27:19 -07003089 final boolean primary = true;
3090 final boolean removable = primaryPhysical;
3091 final boolean emulated = !primaryPhysical;
3092 final long mtpReserveSize = 0L;
3093 final boolean allowMassStorage = false;
3094 final long maxFileSize = 0L;
3095 final UserHandle owner = new UserHandle(userId);
3096 final String uuid = null;
Jeff Sharkey48877892015-03-18 11:27:19 -07003097 final String state = Environment.MEDIA_REMOVED;
3098
Jeff Sharkey5af1835d2015-07-07 17:26:59 -07003099 res.add(0, new StorageVolume(id, StorageVolume.STORAGE_ID_INVALID, path,
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07003100 description, primary, removable, emulated, mtpReserveSize,
3101 allowMassStorage, maxFileSize, owner, uuid, state));
Jeff Sharkey48877892015-03-18 11:27:19 -07003102 }
3103
3104 return res.toArray(new StorageVolume[res.size()]);
Mike Lockwood8fa5f802011-03-24 08:12:30 -07003105 }
3106
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07003107 @Override
3108 public DiskInfo[] getDisks() {
3109 synchronized (mLock) {
3110 final DiskInfo[] res = new DiskInfo[mDisks.size()];
3111 for (int i = 0; i < mDisks.size(); i++) {
3112 res[i] = mDisks.valueAt(i);
3113 }
3114 return res;
3115 }
3116 }
3117
3118 @Override
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07003119 public VolumeInfo[] getVolumes(int flags) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07003120 synchronized (mLock) {
3121 final VolumeInfo[] res = new VolumeInfo[mVolumes.size()];
3122 for (int i = 0; i < mVolumes.size(); i++) {
3123 res[i] = mVolumes.valueAt(i);
3124 }
3125 return res;
3126 }
3127 }
3128
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07003129 @Override
3130 public VolumeRecord[] getVolumeRecords(int flags) {
3131 synchronized (mLock) {
3132 final VolumeRecord[] res = new VolumeRecord[mRecords.size()];
3133 for (int i = 0; i < mRecords.size(); i++) {
3134 res[i] = mRecords.valueAt(i);
3135 }
3136 return res;
3137 }
3138 }
3139
Kenny Rootaf9d6672010-10-08 09:21:39 -07003140 private void addObbStateLocked(ObbState obbState) throws RemoteException {
3141 final IBinder binder = obbState.getBinder();
3142 List<ObbState> obbStates = mObbMounts.get(binder);
Kenny Root5919ac62010-10-05 09:49:40 -07003143
Kenny Rootaf9d6672010-10-08 09:21:39 -07003144 if (obbStates == null) {
3145 obbStates = new ArrayList<ObbState>();
3146 mObbMounts.put(binder, obbStates);
3147 } else {
3148 for (final ObbState o : obbStates) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003149 if (o.rawPath.equals(obbState.rawPath)) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07003150 throw new IllegalStateException("Attempt to add ObbState twice. "
3151 + "This indicates an error in the MountService logic.");
Kenny Root5919ac62010-10-05 09:49:40 -07003152 }
3153 }
Kenny Roota02b8b02010-08-05 16:14:17 -07003154 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07003155
3156 obbStates.add(obbState);
3157 try {
3158 obbState.link();
3159 } catch (RemoteException e) {
3160 /*
3161 * The binder died before we could link it, so clean up our state
3162 * and return failure.
3163 */
3164 obbStates.remove(obbState);
3165 if (obbStates.isEmpty()) {
3166 mObbMounts.remove(binder);
3167 }
3168
3169 // Rethrow the error so mountObb can get it
3170 throw e;
3171 }
3172
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003173 mObbPathToStateMap.put(obbState.rawPath, obbState);
Kenny Roota02b8b02010-08-05 16:14:17 -07003174 }
3175
Kenny Rootaf9d6672010-10-08 09:21:39 -07003176 private void removeObbStateLocked(ObbState obbState) {
3177 final IBinder binder = obbState.getBinder();
3178 final List<ObbState> obbStates = mObbMounts.get(binder);
3179 if (obbStates != null) {
3180 if (obbStates.remove(obbState)) {
3181 obbState.unlink();
Kenny Root05105f72010-09-22 17:29:43 -07003182 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07003183 if (obbStates.isEmpty()) {
3184 mObbMounts.remove(binder);
3185 }
Kenny Roota02b8b02010-08-05 16:14:17 -07003186 }
Kenny Roota02b8b02010-08-05 16:14:17 -07003187
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003188 mObbPathToStateMap.remove(obbState.rawPath);
Kenny Root38cf8862010-09-26 14:18:51 -07003189 }
3190
Kenny Roota02b8b02010-08-05 16:14:17 -07003191 private class ObbActionHandler extends Handler {
3192 private boolean mBound = false;
Kenny Root480afe72010-10-07 10:17:50 -07003193 private final List<ObbAction> mActions = new LinkedList<ObbAction>();
Kenny Roota02b8b02010-08-05 16:14:17 -07003194
3195 ObbActionHandler(Looper l) {
3196 super(l);
3197 }
3198
3199 @Override
3200 public void handleMessage(Message msg) {
3201 switch (msg.what) {
3202 case OBB_RUN_ACTION: {
Kenny Root480afe72010-10-07 10:17:50 -07003203 final ObbAction action = (ObbAction) msg.obj;
Kenny Roota02b8b02010-08-05 16:14:17 -07003204
3205 if (DEBUG_OBB)
3206 Slog.i(TAG, "OBB_RUN_ACTION: " + action.toString());
3207
3208 // If a bind was already initiated we don't really
3209 // need to do anything. The pending install
3210 // will be processed later on.
3211 if (!mBound) {
3212 // If this is the only one pending we might
3213 // have to bind to the service again.
3214 if (!connectToService()) {
3215 Slog.e(TAG, "Failed to bind to media container service");
3216 action.handleError();
3217 return;
Kenny Roota02b8b02010-08-05 16:14:17 -07003218 }
Kenny Roota02b8b02010-08-05 16:14:17 -07003219 }
Kenny Root735de3b2010-09-30 14:11:39 -07003220
Kenny Root735de3b2010-09-30 14:11:39 -07003221 mActions.add(action);
Kenny Roota02b8b02010-08-05 16:14:17 -07003222 break;
3223 }
3224 case OBB_MCS_BOUND: {
3225 if (DEBUG_OBB)
3226 Slog.i(TAG, "OBB_MCS_BOUND");
3227 if (msg.obj != null) {
3228 mContainerService = (IMediaContainerService) msg.obj;
3229 }
3230 if (mContainerService == null) {
3231 // Something seriously wrong. Bail out
3232 Slog.e(TAG, "Cannot bind to media container service");
3233 for (ObbAction action : mActions) {
3234 // Indicate service bind error
3235 action.handleError();
3236 }
3237 mActions.clear();
3238 } else if (mActions.size() > 0) {
Kenny Root480afe72010-10-07 10:17:50 -07003239 final ObbAction action = mActions.get(0);
Kenny Roota02b8b02010-08-05 16:14:17 -07003240 if (action != null) {
3241 action.execute(this);
3242 }
3243 } else {
3244 // Should never happen ideally.
3245 Slog.w(TAG, "Empty queue");
3246 }
3247 break;
3248 }
3249 case OBB_MCS_RECONNECT: {
3250 if (DEBUG_OBB)
3251 Slog.i(TAG, "OBB_MCS_RECONNECT");
3252 if (mActions.size() > 0) {
3253 if (mBound) {
3254 disconnectService();
3255 }
3256 if (!connectToService()) {
3257 Slog.e(TAG, "Failed to bind to media container service");
3258 for (ObbAction action : mActions) {
3259 // Indicate service bind error
3260 action.handleError();
3261 }
3262 mActions.clear();
3263 }
3264 }
3265 break;
3266 }
3267 case OBB_MCS_UNBIND: {
3268 if (DEBUG_OBB)
3269 Slog.i(TAG, "OBB_MCS_UNBIND");
3270
3271 // Delete pending install
3272 if (mActions.size() > 0) {
3273 mActions.remove(0);
3274 }
3275 if (mActions.size() == 0) {
3276 if (mBound) {
3277 disconnectService();
3278 }
3279 } else {
3280 // There are more pending requests in queue.
3281 // Just post MCS_BOUND message to trigger processing
3282 // of next pending install.
3283 mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND);
3284 }
3285 break;
3286 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07003287 case OBB_FLUSH_MOUNT_STATE: {
3288 final String path = (String) msg.obj;
3289
3290 if (DEBUG_OBB)
3291 Slog.i(TAG, "Flushing all OBB state for path " + path);
3292
3293 synchronized (mObbMounts) {
3294 final List<ObbState> obbStatesToRemove = new LinkedList<ObbState>();
3295
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003296 final Iterator<ObbState> i = mObbPathToStateMap.values().iterator();
Kenny Rootaf9d6672010-10-08 09:21:39 -07003297 while (i.hasNext()) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003298 final ObbState state = i.next();
Kenny Rootaf9d6672010-10-08 09:21:39 -07003299
3300 /*
3301 * If this entry's source file is in the volume path
3302 * that got unmounted, remove it because it's no
3303 * longer valid.
3304 */
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003305 if (state.canonicalPath.startsWith(path)) {
3306 obbStatesToRemove.add(state);
Kenny Rootaf9d6672010-10-08 09:21:39 -07003307 }
3308 }
3309
3310 for (final ObbState obbState : obbStatesToRemove) {
3311 if (DEBUG_OBB)
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003312 Slog.i(TAG, "Removing state for " + obbState.rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07003313
3314 removeObbStateLocked(obbState);
3315
3316 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003317 obbState.token.onObbResult(obbState.rawPath, obbState.nonce,
Kenny Rootaf9d6672010-10-08 09:21:39 -07003318 OnObbStateChangeListener.UNMOUNTED);
3319 } catch (RemoteException e) {
3320 Slog.i(TAG, "Couldn't send unmount notification for OBB: "
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003321 + obbState.rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07003322 }
3323 }
3324 }
3325 break;
3326 }
Kenny Roota02b8b02010-08-05 16:14:17 -07003327 }
3328 }
3329
3330 private boolean connectToService() {
3331 if (DEBUG_OBB)
3332 Slog.i(TAG, "Trying to bind to DefaultContainerService");
3333
3334 Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
Jeff Sharkey6dce4962015-07-03 18:08:41 -07003335 if (mContext.bindServiceAsUser(service, mDefContainerConn, Context.BIND_AUTO_CREATE,
Xiaohui Chene4de5a02015-09-22 15:33:31 -07003336 UserHandle.SYSTEM)) {
Kenny Roota02b8b02010-08-05 16:14:17 -07003337 mBound = true;
3338 return true;
3339 }
3340 return false;
3341 }
3342
3343 private void disconnectService() {
3344 mContainerService = null;
3345 mBound = false;
3346 mContext.unbindService(mDefContainerConn);
3347 }
3348 }
3349
3350 abstract class ObbAction {
3351 private static final int MAX_RETRIES = 3;
3352 private int mRetries;
3353
3354 ObbState mObbState;
3355
3356 ObbAction(ObbState obbState) {
3357 mObbState = obbState;
3358 }
3359
3360 public void execute(ObbActionHandler handler) {
3361 try {
3362 if (DEBUG_OBB)
Ben Komalo444eca22011-09-01 15:17:44 -07003363 Slog.i(TAG, "Starting to execute action: " + toString());
Kenny Roota02b8b02010-08-05 16:14:17 -07003364 mRetries++;
3365 if (mRetries > MAX_RETRIES) {
3366 Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
Kenny Root480afe72010-10-07 10:17:50 -07003367 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
Kenny Roota02b8b02010-08-05 16:14:17 -07003368 handleError();
Kenny Roota02b8b02010-08-05 16:14:17 -07003369 } else {
3370 handleExecute();
3371 if (DEBUG_OBB)
3372 Slog.i(TAG, "Posting install MCS_UNBIND");
3373 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
3374 }
3375 } catch (RemoteException e) {
3376 if (DEBUG_OBB)
3377 Slog.i(TAG, "Posting install MCS_RECONNECT");
3378 mObbActionHandler.sendEmptyMessage(OBB_MCS_RECONNECT);
3379 } catch (Exception e) {
3380 if (DEBUG_OBB)
3381 Slog.d(TAG, "Error handling OBB action", e);
3382 handleError();
Kenny Root17eb6fb2010-10-06 15:02:52 -07003383 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
Kenny Roota02b8b02010-08-05 16:14:17 -07003384 }
3385 }
3386
Kenny Root05105f72010-09-22 17:29:43 -07003387 abstract void handleExecute() throws RemoteException, IOException;
Kenny Roota02b8b02010-08-05 16:14:17 -07003388 abstract void handleError();
Kenny Root38cf8862010-09-26 14:18:51 -07003389
3390 protected ObbInfo getObbInfo() throws IOException {
3391 ObbInfo obbInfo;
3392 try {
Xiaohui Chen621b3fc2015-10-02 14:41:42 -07003393 obbInfo = mContainerService.getObbInfo(mObbState.canonicalPath);
Kenny Root38cf8862010-09-26 14:18:51 -07003394 } catch (RemoteException e) {
3395 Slog.d(TAG, "Couldn't call DefaultContainerService to fetch OBB info for "
Xiaohui Chen621b3fc2015-10-02 14:41:42 -07003396 + mObbState.canonicalPath);
Kenny Root38cf8862010-09-26 14:18:51 -07003397 obbInfo = null;
3398 }
3399 if (obbInfo == null) {
Xiaohui Chen621b3fc2015-10-02 14:41:42 -07003400 throw new IOException("Couldn't read OBB file: " + mObbState.canonicalPath);
Kenny Root38cf8862010-09-26 14:18:51 -07003401 }
3402 return obbInfo;
3403 }
3404
Kenny Rootaf9d6672010-10-08 09:21:39 -07003405 protected void sendNewStatusOrIgnore(int status) {
3406 if (mObbState == null || mObbState.token == null) {
3407 return;
3408 }
3409
Kenny Root38cf8862010-09-26 14:18:51 -07003410 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003411 mObbState.token.onObbResult(mObbState.rawPath, mObbState.nonce, status);
Kenny Root38cf8862010-09-26 14:18:51 -07003412 } catch (RemoteException e) {
3413 Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
3414 }
3415 }
Kenny Roota02b8b02010-08-05 16:14:17 -07003416 }
3417
3418 class MountObbAction extends ObbAction {
Ben Komalo444eca22011-09-01 15:17:44 -07003419 private final String mKey;
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003420 private final int mCallingUid;
Kenny Roota02b8b02010-08-05 16:14:17 -07003421
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003422 MountObbAction(ObbState obbState, String key, int callingUid) {
Kenny Roota02b8b02010-08-05 16:14:17 -07003423 super(obbState);
3424 mKey = key;
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003425 mCallingUid = callingUid;
Kenny Roota02b8b02010-08-05 16:14:17 -07003426 }
3427
Jason parks5af0b912010-11-29 09:05:25 -06003428 @Override
Kenny Root735de3b2010-09-30 14:11:39 -07003429 public void handleExecute() throws IOException, RemoteException {
Kenny Rootaf9d6672010-10-08 09:21:39 -07003430 waitForReady();
3431 warnOnNotMounted();
3432
Kenny Root38cf8862010-09-26 14:18:51 -07003433 final ObbInfo obbInfo = getObbInfo();
3434
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003435 if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mCallingUid)) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07003436 Slog.w(TAG, "Denied attempt to mount OBB " + obbInfo.filename
3437 + " which is owned by " + obbInfo.packageName);
3438 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
3439 return;
Kenny Roota02b8b02010-08-05 16:14:17 -07003440 }
3441
Kenny Rootaf9d6672010-10-08 09:21:39 -07003442 final boolean isMounted;
3443 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003444 isMounted = mObbPathToStateMap.containsKey(mObbState.rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07003445 }
3446 if (isMounted) {
3447 Slog.w(TAG, "Attempt to mount OBB which is already mounted: " + obbInfo.filename);
3448 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_ALREADY_MOUNTED);
3449 return;
3450 }
3451
Kenny Rootaf9d6672010-10-08 09:21:39 -07003452 final String hashedKey;
3453 if (mKey == null) {
3454 hashedKey = "none";
3455 } else {
Kenny Rootaf9d6672010-10-08 09:21:39 -07003456 try {
Kenny Root3b1abba2010-10-13 15:00:07 -07003457 SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
3458
3459 KeySpec ks = new PBEKeySpec(mKey.toCharArray(), obbInfo.salt,
3460 PBKDF2_HASH_ROUNDS, CRYPTO_ALGORITHM_KEY_SIZE);
3461 SecretKey key = factory.generateSecret(ks);
3462 BigInteger bi = new BigInteger(key.getEncoded());
3463 hashedKey = bi.toString(16);
Kenny Rootaf9d6672010-10-08 09:21:39 -07003464 } catch (NoSuchAlgorithmException e) {
Kenny Root3b1abba2010-10-13 15:00:07 -07003465 Slog.e(TAG, "Could not load PBKDF2 algorithm", e);
3466 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
3467 return;
3468 } catch (InvalidKeySpecException e) {
3469 Slog.e(TAG, "Invalid key spec when loading PBKDF2 algorithm", e);
3470 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
Kenny Root38cf8862010-09-26 14:18:51 -07003471 return;
3472 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07003473 }
Kenny Root38cf8862010-09-26 14:18:51 -07003474
Kenny Rootaf9d6672010-10-08 09:21:39 -07003475 int rc = StorageResultCode.OperationSucceeded;
Kenny Rootaf9d6672010-10-08 09:21:39 -07003476 try {
Xiaohui Chen621b3fc2015-10-02 14:41:42 -07003477 mConnector.execute("obb", "mount", mObbState.canonicalPath, new SensitiveArg(hashedKey),
Jeff Sharkey56cd6462013-06-07 15:09:15 -07003478 mObbState.ownerGid);
Kenny Rootaf9d6672010-10-08 09:21:39 -07003479 } catch (NativeDaemonConnectorException e) {
3480 int code = e.getCode();
3481 if (code != VoldResponseCode.OpFailedStorageBusy) {
3482 rc = StorageResultCode.OperationFailedInternalError;
Kenny Roota02b8b02010-08-05 16:14:17 -07003483 }
3484 }
3485
Kenny Rootaf9d6672010-10-08 09:21:39 -07003486 if (rc == StorageResultCode.OperationSucceeded) {
3487 if (DEBUG_OBB)
Xiaohui Chen621b3fc2015-10-02 14:41:42 -07003488 Slog.d(TAG, "Successfully mounted OBB " + mObbState.canonicalPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07003489
3490 synchronized (mObbMounts) {
3491 addObbStateLocked(mObbState);
3492 }
3493
3494 sendNewStatusOrIgnore(OnObbStateChangeListener.MOUNTED);
Kenny Root02c87302010-07-01 08:10:18 -07003495 } else {
Kenny Root05105f72010-09-22 17:29:43 -07003496 Slog.e(TAG, "Couldn't mount OBB file: " + rc);
Kenny Roota02b8b02010-08-05 16:14:17 -07003497
Kenny Rootaf9d6672010-10-08 09:21:39 -07003498 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT);
Kenny Root02c87302010-07-01 08:10:18 -07003499 }
3500 }
3501
Jason parks5af0b912010-11-29 09:05:25 -06003502 @Override
Kenny Roota02b8b02010-08-05 16:14:17 -07003503 public void handleError() {
Kenny Rootaf9d6672010-10-08 09:21:39 -07003504 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
Kenny Root02c87302010-07-01 08:10:18 -07003505 }
Kenny Roota02b8b02010-08-05 16:14:17 -07003506
3507 @Override
3508 public String toString() {
3509 StringBuilder sb = new StringBuilder();
3510 sb.append("MountObbAction{");
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003511 sb.append(mObbState);
Kenny Roota02b8b02010-08-05 16:14:17 -07003512 sb.append('}');
3513 return sb.toString();
3514 }
3515 }
3516
3517 class UnmountObbAction extends ObbAction {
Ben Komalo444eca22011-09-01 15:17:44 -07003518 private final boolean mForceUnmount;
Kenny Roota02b8b02010-08-05 16:14:17 -07003519
3520 UnmountObbAction(ObbState obbState, boolean force) {
3521 super(obbState);
3522 mForceUnmount = force;
3523 }
3524
Jason parks5af0b912010-11-29 09:05:25 -06003525 @Override
Kenny Root38cf8862010-09-26 14:18:51 -07003526 public void handleExecute() throws IOException {
Kenny Rootaf9d6672010-10-08 09:21:39 -07003527 waitForReady();
3528 warnOnNotMounted();
3529
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003530 final ObbState existingState;
Kenny Root38cf8862010-09-26 14:18:51 -07003531 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003532 existingState = mObbPathToStateMap.get(mObbState.rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07003533 }
Kenny Root38cf8862010-09-26 14:18:51 -07003534
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003535 if (existingState == null) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07003536 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_NOT_MOUNTED);
3537 return;
3538 }
3539
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003540 if (existingState.ownerGid != mObbState.ownerGid) {
3541 Slog.w(TAG, "Permission denied attempting to unmount OBB " + existingState.rawPath
3542 + " (owned by GID " + existingState.ownerGid + ")");
Kenny Rootaf9d6672010-10-08 09:21:39 -07003543 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
3544 return;
3545 }
3546
Kenny Rootaf9d6672010-10-08 09:21:39 -07003547 int rc = StorageResultCode.OperationSucceeded;
Kenny Rootaf9d6672010-10-08 09:21:39 -07003548 try {
Xiaohui Chen621b3fc2015-10-02 14:41:42 -07003549 final Command cmd = new Command("obb", "unmount", mObbState.canonicalPath);
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08003550 if (mForceUnmount) {
3551 cmd.appendArg("force");
3552 }
3553 mConnector.execute(cmd);
Kenny Rootaf9d6672010-10-08 09:21:39 -07003554 } catch (NativeDaemonConnectorException e) {
3555 int code = e.getCode();
3556 if (code == VoldResponseCode.OpFailedStorageBusy) {
3557 rc = StorageResultCode.OperationFailedStorageBusy;
3558 } else if (code == VoldResponseCode.OpFailedStorageNotFound) {
3559 // If it's not mounted then we've already won.
3560 rc = StorageResultCode.OperationSucceeded;
3561 } else {
3562 rc = StorageResultCode.OperationFailedInternalError;
Kenny Roota02b8b02010-08-05 16:14:17 -07003563 }
3564 }
3565
Kenny Rootaf9d6672010-10-08 09:21:39 -07003566 if (rc == StorageResultCode.OperationSucceeded) {
3567 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003568 removeObbStateLocked(existingState);
Kenny Root38cf8862010-09-26 14:18:51 -07003569 }
3570
Kenny Rootaf9d6672010-10-08 09:21:39 -07003571 sendNewStatusOrIgnore(OnObbStateChangeListener.UNMOUNTED);
Kenny Roota02b8b02010-08-05 16:14:17 -07003572 } else {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003573 Slog.w(TAG, "Could not unmount OBB: " + existingState);
Kenny Rootaf9d6672010-10-08 09:21:39 -07003574 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_UNMOUNT);
Kenny Roota02b8b02010-08-05 16:14:17 -07003575 }
3576 }
3577
Jason parks5af0b912010-11-29 09:05:25 -06003578 @Override
Kenny Roota02b8b02010-08-05 16:14:17 -07003579 public void handleError() {
Kenny Rootaf9d6672010-10-08 09:21:39 -07003580 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
Kenny Roota02b8b02010-08-05 16:14:17 -07003581 }
3582
3583 @Override
3584 public String toString() {
3585 StringBuilder sb = new StringBuilder();
3586 sb.append("UnmountObbAction{");
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07003587 sb.append(mObbState);
Kenny Roota02b8b02010-08-05 16:14:17 -07003588 sb.append(",force=");
3589 sb.append(mForceUnmount);
Kenny Roota02b8b02010-08-05 16:14:17 -07003590 sb.append('}');
3591 return sb.toString();
3592 }
Kenny Root02c87302010-07-01 08:10:18 -07003593 }
Kenny Root38cf8862010-09-26 14:18:51 -07003594
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07003595 private static class Callbacks extends Handler {
3596 private static final int MSG_STORAGE_STATE_CHANGED = 1;
3597 private static final int MSG_VOLUME_STATE_CHANGED = 2;
Jeff Sharkey50a05452015-04-29 11:24:52 -07003598 private static final int MSG_VOLUME_RECORD_CHANGED = 3;
3599 private static final int MSG_VOLUME_FORGOTTEN = 4;
3600 private static final int MSG_DISK_SCANNED = 5;
Makoto Onuki9dc575d2015-06-12 16:10:25 -07003601 private static final int MSG_DISK_DESTROYED = 6;
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07003602
3603 private final RemoteCallbackList<IMountServiceListener>
3604 mCallbacks = new RemoteCallbackList<>();
3605
3606 public Callbacks(Looper looper) {
3607 super(looper);
3608 }
3609
3610 public void register(IMountServiceListener callback) {
3611 mCallbacks.register(callback);
3612 }
3613
3614 public void unregister(IMountServiceListener callback) {
3615 mCallbacks.unregister(callback);
3616 }
3617
3618 @Override
3619 public void handleMessage(Message msg) {
3620 final SomeArgs args = (SomeArgs) msg.obj;
3621 final int n = mCallbacks.beginBroadcast();
3622 for (int i = 0; i < n; i++) {
3623 final IMountServiceListener callback = mCallbacks.getBroadcastItem(i);
3624 try {
3625 invokeCallback(callback, msg.what, args);
3626 } catch (RemoteException ignored) {
3627 }
3628 }
3629 mCallbacks.finishBroadcast();
3630 args.recycle();
3631 }
3632
3633 private void invokeCallback(IMountServiceListener callback, int what, SomeArgs args)
3634 throws RemoteException {
3635 switch (what) {
3636 case MSG_STORAGE_STATE_CHANGED: {
3637 callback.onStorageStateChanged((String) args.arg1, (String) args.arg2,
3638 (String) args.arg3);
3639 break;
3640 }
3641 case MSG_VOLUME_STATE_CHANGED: {
3642 callback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3);
3643 break;
3644 }
Jeff Sharkey50a05452015-04-29 11:24:52 -07003645 case MSG_VOLUME_RECORD_CHANGED: {
3646 callback.onVolumeRecordChanged((VolumeRecord) args.arg1);
3647 break;
3648 }
3649 case MSG_VOLUME_FORGOTTEN: {
3650 callback.onVolumeForgotten((String) args.arg1);
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07003651 break;
3652 }
Jeff Sharkey620b32b2015-04-23 19:36:02 -07003653 case MSG_DISK_SCANNED: {
3654 callback.onDiskScanned((DiskInfo) args.arg1, args.argi2);
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07003655 break;
3656 }
Makoto Onuki9dc575d2015-06-12 16:10:25 -07003657 case MSG_DISK_DESTROYED: {
3658 callback.onDiskDestroyed((DiskInfo) args.arg1);
3659 break;
3660 }
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07003661 }
3662 }
3663
3664 private void notifyStorageStateChanged(String path, String oldState, String newState) {
3665 final SomeArgs args = SomeArgs.obtain();
3666 args.arg1 = path;
3667 args.arg2 = oldState;
3668 args.arg3 = newState;
3669 obtainMessage(MSG_STORAGE_STATE_CHANGED, args).sendToTarget();
3670 }
3671
3672 private void notifyVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
3673 final SomeArgs args = SomeArgs.obtain();
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07003674 args.arg1 = vol.clone();
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07003675 args.argi2 = oldState;
3676 args.argi3 = newState;
3677 obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget();
3678 }
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07003679
Jeff Sharkey50a05452015-04-29 11:24:52 -07003680 private void notifyVolumeRecordChanged(VolumeRecord rec) {
3681 final SomeArgs args = SomeArgs.obtain();
3682 args.arg1 = rec.clone();
3683 obtainMessage(MSG_VOLUME_RECORD_CHANGED, args).sendToTarget();
3684 }
3685
3686 private void notifyVolumeForgotten(String fsUuid) {
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07003687 final SomeArgs args = SomeArgs.obtain();
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07003688 args.arg1 = fsUuid;
Jeff Sharkey50a05452015-04-29 11:24:52 -07003689 obtainMessage(MSG_VOLUME_FORGOTTEN, args).sendToTarget();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07003690 }
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07003691
Jeff Sharkey620b32b2015-04-23 19:36:02 -07003692 private void notifyDiskScanned(DiskInfo disk, int volumeCount) {
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07003693 final SomeArgs args = SomeArgs.obtain();
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07003694 args.arg1 = disk.clone();
Jeff Sharkey620b32b2015-04-23 19:36:02 -07003695 args.argi2 = volumeCount;
3696 obtainMessage(MSG_DISK_SCANNED, args).sendToTarget();
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07003697 }
Makoto Onuki9dc575d2015-06-12 16:10:25 -07003698
3699 private void notifyDiskDestroyed(DiskInfo disk) {
3700 final SomeArgs args = SomeArgs.obtain();
3701 args.arg1 = disk.clone();
3702 obtainMessage(MSG_DISK_DESTROYED, args).sendToTarget();
3703 }
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07003704 }
3705
Kenny Root38cf8862010-09-26 14:18:51 -07003706 @Override
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003707 protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
3708 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
3709
3710 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ", 160);
Jeff Sharkey27de30d2015-04-18 16:20:27 -07003711 synchronized (mLock) {
3712 pw.println("Disks:");
3713 pw.increaseIndent();
3714 for (int i = 0; i < mDisks.size(); i++) {
3715 final DiskInfo disk = mDisks.valueAt(i);
3716 disk.dump(pw);
3717 }
3718 pw.decreaseIndent();
3719
3720 pw.println();
3721 pw.println("Volumes:");
3722 pw.increaseIndent();
3723 for (int i = 0; i < mVolumes.size(); i++) {
3724 final VolumeInfo vol = mVolumes.valueAt(i);
3725 if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.id)) continue;
3726 vol.dump(pw);
3727 }
3728 pw.decreaseIndent();
3729
3730 pw.println();
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07003731 pw.println("Records:");
Jeff Sharkey27de30d2015-04-18 16:20:27 -07003732 pw.increaseIndent();
Jeff Sharkeyb36586a2015-04-27 08:42:28 -07003733 for (int i = 0; i < mRecords.size(); i++) {
3734 final VolumeRecord note = mRecords.valueAt(i);
3735 note.dump(pw);
Jeff Sharkey27de30d2015-04-18 16:20:27 -07003736 }
3737 pw.decreaseIndent();
Jeff Sharkey275e3e42015-04-24 16:10:32 -07003738
3739 pw.println();
3740 pw.println("Primary storage UUID: " + mPrimaryStorageUuid);
Jeff Sharkey4c099d02015-05-15 13:45:00 -07003741 pw.println("Force adoptable: " + mForceAdoptable);
Jeff Sharkeyf9fc6d62015-11-08 16:46:05 -08003742 pw.println();
Jeff Sharkeyce14cd02015-12-07 15:35:42 -07003743 pw.println("Local unlocked users: " + Arrays.toString(mLocalUnlockedUsers));
3744 pw.println("System unlocked users: " + Arrays.toString(mSystemUnlockedUsers));
Jeff Sharkey27de30d2015-04-18 16:20:27 -07003745 }
Kenny Root38cf8862010-09-26 14:18:51 -07003746
Kenny Root38cf8862010-09-26 14:18:51 -07003747 synchronized (mObbMounts) {
Jeff Sharkey27de30d2015-04-18 16:20:27 -07003748 pw.println();
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003749 pw.println("mObbMounts:");
3750 pw.increaseIndent();
3751 final Iterator<Entry<IBinder, List<ObbState>>> binders = mObbMounts.entrySet()
3752 .iterator();
Kenny Rootaf9d6672010-10-08 09:21:39 -07003753 while (binders.hasNext()) {
3754 Entry<IBinder, List<ObbState>> e = binders.next();
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003755 pw.println(e.getKey() + ":");
3756 pw.increaseIndent();
Kenny Rootaf9d6672010-10-08 09:21:39 -07003757 final List<ObbState> obbStates = e.getValue();
Kenny Root38cf8862010-09-26 14:18:51 -07003758 for (final ObbState obbState : obbStates) {
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003759 pw.println(obbState);
Kenny Root38cf8862010-09-26 14:18:51 -07003760 }
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003761 pw.decreaseIndent();
Kenny Root38cf8862010-09-26 14:18:51 -07003762 }
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003763 pw.decreaseIndent();
Kenny Rootaf9d6672010-10-08 09:21:39 -07003764
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003765 pw.println();
3766 pw.println("mObbPathToStateMap:");
3767 pw.increaseIndent();
Kenny Rootaf9d6672010-10-08 09:21:39 -07003768 final Iterator<Entry<String, ObbState>> maps = mObbPathToStateMap.entrySet().iterator();
3769 while (maps.hasNext()) {
3770 final Entry<String, ObbState> e = maps.next();
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003771 pw.print(e.getKey());
3772 pw.print(" -> ");
3773 pw.println(e.getValue());
Kenny Rootaf9d6672010-10-08 09:21:39 -07003774 }
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003775 pw.decreaseIndent();
Kenny Root38cf8862010-09-26 14:18:51 -07003776 }
Kenny Root4161f9b2011-07-13 09:48:33 -07003777
Robert Greenwalt470fd722012-01-18 12:51:15 -08003778 pw.println();
Jeff Sharkey5b0e5202015-12-18 17:18:09 -07003779 pw.println("mConnector:");
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003780 pw.increaseIndent();
Robert Greenwalt470fd722012-01-18 12:51:15 -08003781 mConnector.dump(fd, pw, args);
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07003782 pw.decreaseIndent();
Christopher Tate7265abe2014-11-21 13:54:45 -08003783
Christopher Tate7265abe2014-11-21 13:54:45 -08003784 pw.println();
Jeff Sharkey5b0e5202015-12-18 17:18:09 -07003785 pw.println("mCryptConnector:");
3786 pw.increaseIndent();
3787 mCryptConnector.dump(fd, pw, args);
3788 pw.decreaseIndent();
3789
3790 pw.println();
Christopher Tate7265abe2014-11-21 13:54:45 -08003791 pw.print("Last maintenance: ");
Jeff Sharkeye8a4b662015-06-27 15:43:45 -07003792 pw.println(TimeUtils.formatForLogging(mLastMaintenance));
Kenny Root38cf8862010-09-26 14:18:51 -07003793 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003794
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -07003795 /** {@inheritDoc} */
Jeff Sharkey48877892015-03-18 11:27:19 -07003796 @Override
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -07003797 public void monitor() {
3798 if (mConnector != null) {
3799 mConnector.monitor();
3800 }
Paul Lawrence1c62cbb2015-06-03 14:14:52 -07003801 if (mCryptConnector != null) {
3802 mCryptConnector.monitor();
3803 }
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -07003804 }
Svet Ganov6ee871e2015-07-10 14:29:33 -07003805
3806 private final class MountServiceInternalImpl extends MountServiceInternal {
3807 // Not guarded by a lock.
3808 private final CopyOnWriteArrayList<ExternalStorageMountPolicy> mPolicies =
3809 new CopyOnWriteArrayList<>();
3810
3811 @Override
3812 public void addExternalStoragePolicy(ExternalStorageMountPolicy policy) {
3813 // No locking - CopyOnWriteArrayList
3814 mPolicies.add(policy);
3815 }
3816
3817 @Override
3818 public void onExternalStoragePolicyChanged(int uid, String packageName) {
3819 final int mountMode = getExternalStorageMountMode(uid, packageName);
3820 remountUidExternalStorage(uid, mountMode);
3821 }
3822
3823 @Override
3824 public int getExternalStorageMountMode(int uid, String packageName) {
3825 // No locking - CopyOnWriteArrayList
3826 int mountMode = Integer.MAX_VALUE;
3827 for (ExternalStorageMountPolicy policy : mPolicies) {
3828 final int policyMode = policy.getMountMode(uid, packageName);
3829 if (policyMode == Zygote.MOUNT_EXTERNAL_NONE) {
3830 return Zygote.MOUNT_EXTERNAL_NONE;
3831 }
3832 mountMode = Math.min(mountMode, policyMode);
3833 }
3834 if (mountMode == Integer.MAX_VALUE) {
3835 return Zygote.MOUNT_EXTERNAL_NONE;
3836 }
3837 return mountMode;
3838 }
3839
3840 public boolean hasExternalStorage(int uid, String packageName) {
Amith Yamasani2bd5cff2015-07-22 14:42:31 -07003841 // No need to check for system uid. This avoids a deadlock between
3842 // PackageManagerService and AppOpsService.
3843 if (uid == Process.SYSTEM_UID) {
3844 return true;
3845 }
Svet Ganov6ee871e2015-07-10 14:29:33 -07003846 // No locking - CopyOnWriteArrayList
3847 for (ExternalStorageMountPolicy policy : mPolicies) {
3848 final boolean policyHasStorage = policy.hasExternalStorage(uid, packageName);
3849 if (!policyHasStorage) {
3850 return false;
3851 }
3852 }
3853 return true;
3854 }
3855 }
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -07003856}