blob: f88802a73f5cf0501770207bda83dacc8c0d6277 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070019import static com.android.internal.util.XmlUtils.readIntAttribute;
20import static com.android.internal.util.XmlUtils.readStringAttribute;
21import static com.android.internal.util.XmlUtils.writeIntAttribute;
22import static com.android.internal.util.XmlUtils.writeStringAttribute;
23import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
24import static org.xmlpull.v1.XmlPullParser.START_TAG;
25
Jason parks8888c592011-01-20 22:46:41 -060026import android.Manifest;
Elliott Hughesf839b4f2014-09-26 12:30:47 -070027import android.app.ActivityManagerNative;
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -070028import android.app.AppOpsManager;
Kenny Roota02b8b02010-08-05 16:14:17 -070029import android.content.ComponentName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.content.Context;
31import android.content.Intent;
Kenny Roota02b8b02010-08-05 16:14:17 -070032import android.content.ServiceConnection;
Elliott Hughesf839b4f2014-09-26 12:30:47 -070033import android.content.res.Configuration;
Kenny Root02c87302010-07-01 08:10:18 -070034import android.content.res.ObbInfo;
Jeff Sharkey48877892015-03-18 11:27:19 -070035import android.mtp.MtpStorage;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.net.Uri;
Kenny Root02c87302010-07-01 08:10:18 -070037import android.os.Binder;
Kenny Roota02b8b02010-08-05 16:14:17 -070038import android.os.Environment;
Jeff Sharkeyb049e212012-09-07 23:16:01 -070039import android.os.Environment.UserEnvironment;
Jeff Sharkey48877892015-03-18 11:27:19 -070040import android.os.FileUtils;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080041import android.os.Handler;
Dianne Hackbornefa92b22013-05-03 14:11:43 -070042import android.os.HandlerThread;
Kenny Roota02b8b02010-08-05 16:14:17 -070043import android.os.IBinder;
Daniel Sandler5f27ef42010-03-16 15:42:02 -040044import android.os.Looper;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080045import android.os.Message;
Jeff Sharkey7151a9a2015-04-04 15:22:37 -070046import android.os.RemoteCallbackList;
San Mehat4270e1e2010-01-29 05:32:19 -080047import android.os.RemoteException;
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -080048import android.os.ServiceManager;
Svetoslavf23b64d2013-04-25 14:45:54 -070049import android.os.SystemClock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050import android.os.SystemProperties;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070051import android.os.UserHandle;
Emily Bernier92aa5a22014-07-07 10:11:48 -040052import android.os.UserManager;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070053import android.os.storage.DiskInfo;
Kenny Roota02b8b02010-08-05 16:14:17 -070054import android.os.storage.IMountService;
55import android.os.storage.IMountServiceListener;
56import android.os.storage.IMountShutdownObserver;
57import android.os.storage.IObbActionListener;
Kenny Rootaf9d6672010-10-08 09:21:39 -070058import android.os.storage.OnObbStateChangeListener;
Paul Lawrence46791e72014-04-03 09:10:26 -070059import android.os.storage.StorageManager;
Kenny Roota02b8b02010-08-05 16:14:17 -070060import android.os.storage.StorageResultCode;
Mike Lockwood2f6a3882011-05-09 19:08:06 -070061import android.os.storage.StorageVolume;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070062import android.os.storage.VolumeInfo;
Jason parksf7b3cd42011-01-27 09:28:25 -060063import android.text.TextUtils;
Jeff Sharkey1783f142015-04-17 10:52:51 -070064import android.text.format.DateUtils;
Jeff Sharkey48877892015-03-18 11:27:19 -070065import android.util.ArrayMap;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070066import android.util.AtomicFile;
67import android.util.DebugUtils;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070068import android.util.Log;
San Mehata5078592010-03-25 09:36:54 -070069import android.util.Slog;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070070import android.util.Xml;
Jeff Sharkey48877892015-03-18 11:27:19 -070071
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070072import libcore.io.IoUtils;
Jeff Sharkey48877892015-03-18 11:27:19 -070073import libcore.util.EmptyArray;
74import libcore.util.HexEncoding;
Mike Lockwood2f6a3882011-05-09 19:08:06 -070075
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -080076import com.android.internal.annotations.GuardedBy;
77import com.android.internal.annotations.VisibleForTesting;
Jeff Sharkeyb049e212012-09-07 23:16:01 -070078import com.android.internal.app.IMediaContainerService;
Jeff Sharkey7151a9a2015-04-04 15:22:37 -070079import com.android.internal.os.SomeArgs;
Jeff Sharkey48877892015-03-18 11:27:19 -070080import com.android.internal.util.ArrayUtils;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070081import com.android.internal.util.FastXmlSerializer;
Jeff Sharkey5aca2b82013-10-16 16:21:54 -070082import com.android.internal.util.IndentingPrintWriter;
Jeff Sharkey4fbbda42012-09-24 18:34:07 -070083import com.android.internal.util.Preconditions;
Jeff Sharkeyb049e212012-09-07 23:16:01 -070084import com.android.server.NativeDaemonConnector.Command;
Jeff Sharkey56cd6462013-06-07 15:09:15 -070085import com.android.server.NativeDaemonConnector.SensitiveArg;
Jeff Sharkeyb049e212012-09-07 23:16:01 -070086import com.android.server.pm.PackageManagerService;
Kenny Roota02b8b02010-08-05 16:14:17 -070087
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070088import org.xmlpull.v1.XmlPullParser;
89import org.xmlpull.v1.XmlPullParserException;
90import org.xmlpull.v1.XmlSerializer;
91
Jeff Sharkeyb049e212012-09-07 23:16:01 -070092import java.io.File;
Kenny Root38cf8862010-09-26 14:18:51 -070093import java.io.FileDescriptor;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070094import java.io.FileInputStream;
95import java.io.FileNotFoundException;
Christopher Tate7265abe2014-11-21 13:54:45 -080096import java.io.FileOutputStream;
Kenny Root05105f72010-09-22 17:29:43 -070097import java.io.IOException;
Kenny Root38cf8862010-09-26 14:18:51 -070098import java.io.PrintWriter;
Kenny Root3b1abba2010-10-13 15:00:07 -070099import java.math.BigInteger;
Paul Lawrence8e397362014-01-27 15:22:30 -0800100import java.nio.charset.StandardCharsets;
Kenny Root735de3b2010-09-30 14:11:39 -0700101import java.security.NoSuchAlgorithmException;
Kenny Root3b1abba2010-10-13 15:00:07 -0700102import java.security.spec.InvalidKeySpecException;
103import java.security.spec.KeySpec;
Christopher Tate7265abe2014-11-21 13:54:45 -0800104import java.text.SimpleDateFormat;
San Mehat22dd86e2010-01-12 12:21:18 -0800105import java.util.ArrayList;
Christopher Tate7265abe2014-11-21 13:54:45 -0800106import java.util.Date;
Kenny Roota02b8b02010-08-05 16:14:17 -0700107import java.util.HashMap;
San Mehat6cdd9c02010-02-09 14:45:20 -0800108import java.util.HashSet;
Kenny Root38cf8862010-09-26 14:18:51 -0700109import java.util.Iterator;
Kenny Roota02b8b02010-08-05 16:14:17 -0700110import java.util.LinkedList;
111import java.util.List;
Elliott Hughesf839b4f2014-09-26 12:30:47 -0700112import java.util.Locale;
Kenny Roota02b8b02010-08-05 16:14:17 -0700113import java.util.Map;
Kenny Root38cf8862010-09-26 14:18:51 -0700114import java.util.Map.Entry;
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700115import java.util.Objects;
Kenny Root51a573c2012-05-17 13:30:28 -0700116import java.util.concurrent.CountDownLatch;
117import java.util.concurrent.TimeUnit;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800118
Kenny Root3b1abba2010-10-13 15:00:07 -0700119import javax.crypto.SecretKey;
120import javax.crypto.SecretKeyFactory;
121import javax.crypto.spec.PBEKeySpec;
122
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800123/**
Jeff Sharkey48877892015-03-18 11:27:19 -0700124 * Service responsible for various storage media. Connects to {@code vold} to
125 * watch for and manage dynamically added storage, such as SD cards and USB mass
126 * storage. Also decides how storage should be presented to users on the device.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127 */
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -0700128class MountService extends IMountService.Stub
129 implements INativeDaemonConnectorCallbacks, Watchdog.Monitor {
Jason parks5af0b912010-11-29 09:05:25 -0600130
Jeff Sharkey48877892015-03-18 11:27:19 -0700131 // TODO: finish enforcing UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA
132
Christopher Tated417d622013-08-19 16:14:25 -0700133 // Static direct instance pointer for the tightly-coupled idle service to use
134 static MountService sSelf = null;
135
Jeff Sharkey56e62932015-03-21 20:41:00 -0700136 public static class Lifecycle extends SystemService {
137 private MountService mMountService;
138
139 public Lifecycle(Context context) {
140 super(context);
141 }
142
143 @Override
144 public void onStart() {
145 mMountService = new MountService(getContext());
146 publishBinderService("mount", mMountService);
147 }
148
149 @Override
150 public void onBootPhase(int phase) {
151 if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
152 mMountService.systemReady();
153 }
154 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700155
156 @Override
157 public void onStartUser(int userHandle) {
158 mMountService.onStartUser(userHandle);
159 }
160
161 @Override
162 public void onCleanupUser(int userHandle) {
163 mMountService.onCleanupUser(userHandle);
164 }
Jeff Sharkey56e62932015-03-21 20:41:00 -0700165 }
166
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800167 private static final boolean LOCAL_LOGD = false;
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800168 private static final boolean DEBUG_EVENTS = false;
Kenny Rootb7db2722011-01-25 16:39:35 -0800169 private static final boolean DEBUG_OBB = false;
Kenny Root02c87302010-07-01 08:10:18 -0700170
Kenny Root07714d42011-08-17 17:49:28 -0700171 // Disable this since it messes up long-running cryptfs operations.
172 private static final boolean WATCHDOG_ENABLE = false;
173
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800174 private static final String TAG = "MountService";
175
Kenny Root305bcbf2010-09-03 07:56:38 -0700176 private static final String VOLD_TAG = "VoldConnector";
177
Kenny Rootcf0b38c2011-03-22 14:17:59 -0700178 /** Maximum number of ASEC containers allowed to be mounted. */
179 private static final int MAX_CONTAINERS = 250;
180
San Mehat4270e1e2010-01-29 05:32:19 -0800181 /*
182 * Internal vold response code constants
183 */
San Mehat22dd86e2010-01-12 12:21:18 -0800184 class VoldResponseCode {
San Mehat4270e1e2010-01-29 05:32:19 -0800185 /*
186 * 100 series - Requestion action was initiated; expect another reply
187 * before proceeding with a new command.
188 */
San Mehat22dd86e2010-01-12 12:21:18 -0800189 public static final int VolumeListResult = 110;
190 public static final int AsecListResult = 111;
San Mehatc1b4ce92010-02-16 17:13:03 -0800191 public static final int StorageUsersListResult = 112;
Paul Lawrencee51dcf92014-03-18 10:56:00 -0700192 public static final int CryptfsGetfieldResult = 113;
San Mehat22dd86e2010-01-12 12:21:18 -0800193
San Mehat4270e1e2010-01-29 05:32:19 -0800194 /*
195 * 200 series - Requestion action has been successfully completed.
196 */
197 public static final int ShareStatusResult = 210;
San Mehat22dd86e2010-01-12 12:21:18 -0800198 public static final int AsecPathResult = 211;
San Mehat4270e1e2010-01-29 05:32:19 -0800199 public static final int ShareEnabledResult = 212;
San Mehat22dd86e2010-01-12 12:21:18 -0800200
San Mehat4270e1e2010-01-29 05:32:19 -0800201 /*
202 * 400 series - Command was accepted, but the requested action
203 * did not take place.
204 */
205 public static final int OpFailedNoMedia = 401;
206 public static final int OpFailedMediaBlank = 402;
207 public static final int OpFailedMediaCorrupt = 403;
208 public static final int OpFailedVolNotMounted = 404;
San Mehatd9709982010-02-18 11:43:03 -0800209 public static final int OpFailedStorageBusy = 405;
San Mehat2d66cef2010-03-23 11:12:52 -0700210 public static final int OpFailedStorageNotFound = 406;
San Mehat4270e1e2010-01-29 05:32:19 -0800211
212 /*
213 * 600 series - Unsolicited broadcasts.
214 */
Jeff Sharkey48877892015-03-18 11:27:19 -0700215 public static final int DISK_CREATED = 640;
216 public static final int DISK_SIZE_CHANGED = 641;
217 public static final int DISK_LABEL_CHANGED = 642;
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700218 public static final int DISK_SCANNED = 643;
Jeff Sharkey48877892015-03-18 11:27:19 -0700219 public static final int DISK_DESTROYED = 649;
220
221 public static final int VOLUME_CREATED = 650;
222 public static final int VOLUME_STATE_CHANGED = 651;
223 public static final int VOLUME_FS_TYPE_CHANGED = 652;
224 public static final int VOLUME_FS_UUID_CHANGED = 653;
225 public static final int VOLUME_FS_LABEL_CHANGED = 654;
226 public static final int VOLUME_PATH_CHANGED = 655;
227 public static final int VOLUME_DESTROYED = 659;
Svetoslavf23b64d2013-04-25 14:45:54 -0700228
229 /*
230 * 700 series - fstrim
231 */
232 public static final int FstrimCompleted = 700;
San Mehat22dd86e2010-01-12 12:21:18 -0800233 }
234
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700235 private static final String TAG_VOLUMES = "volumes";
236 private static final String TAG_VOLUME = "volume";
237 private static final String ATTR_TYPE = "type";
238 private static final String ATTR_FS_UUID = "fsUuid";
239 private static final String ATTR_NICKNAME = "nickname";
240 private static final String ATTR_USER_FLAGS = "userFlags";
241
242 private final AtomicFile mMetadataFile;
243
244 private static class VolumeMetadata {
245 public final int type;
246 public final String fsUuid;
247 public String nickname;
248 public int userFlags;
249
250 public VolumeMetadata(int type, String fsUuid) {
251 this.type = type;
252 this.fsUuid = Preconditions.checkNotNull(fsUuid);
253 }
254
255 public static VolumeMetadata read(XmlPullParser in) throws IOException {
256 final int type = readIntAttribute(in, ATTR_TYPE);
257 final String fsUuid = readStringAttribute(in, ATTR_FS_UUID);
258 final VolumeMetadata meta = new VolumeMetadata(type, fsUuid);
259 meta.nickname = readStringAttribute(in, ATTR_NICKNAME);
260 meta.userFlags = readIntAttribute(in, ATTR_USER_FLAGS);
261 return meta;
262 }
263
264 public static void write(XmlSerializer out, VolumeMetadata meta) throws IOException {
265 out.startTag(null, TAG_VOLUME);
266 writeIntAttribute(out, ATTR_TYPE, meta.type);
267 writeStringAttribute(out, ATTR_FS_UUID, meta.fsUuid);
268 writeStringAttribute(out, ATTR_NICKNAME, meta.nickname);
269 writeIntAttribute(out, ATTR_USER_FLAGS, meta.userFlags);
270 out.endTag(null, TAG_VOLUME);
271 }
272
273 public void dump(IndentingPrintWriter pw) {
274 pw.println("VolumeMetadata:");
275 pw.increaseIndent();
276 pw.printPair("type", DebugUtils.valueToString(VolumeInfo.class, "TYPE_", type));
277 pw.printPair("fsUuid", fsUuid);
278 pw.printPair("nickname", nickname);
279 pw.printPair("userFlags",
280 DebugUtils.flagsToString(VolumeInfo.class, "USER_FLAG_", userFlags));
281 pw.decreaseIndent();
282 pw.println();
283 }
284 }
285
Jeff Sharkey48877892015-03-18 11:27:19 -0700286 /**
287 * <em>Never</em> hold the lock while performing downcalls into vold, since
288 * unsolicited events can suddenly appear to update data structures.
289 */
290 private final Object mLock = new Object();
291
292 @GuardedBy("mLock")
293 private int[] mStartedUsers = EmptyArray.INT;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700294
295 /** Map from disk ID to disk */
Jeff Sharkey48877892015-03-18 11:27:19 -0700296 @GuardedBy("mLock")
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700297 private ArrayMap<String, DiskInfo> mDisks = new ArrayMap<>();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700298 /** Map from volume ID to disk */
Jeff Sharkey48877892015-03-18 11:27:19 -0700299 @GuardedBy("mLock")
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700300 private ArrayMap<String, VolumeInfo> mVolumes = new ArrayMap<>();
Jeff Sharkey48877892015-03-18 11:27:19 -0700301
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700302 /** Map from UUID to metadata */
303 @GuardedBy("mLock")
304 private ArrayMap<String, VolumeMetadata> mMetadata = new ArrayMap<>();
305
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700306 /** Map from disk ID to latches */
307 @GuardedBy("mLock")
308 private ArrayMap<String, CountDownLatch> mDiskScanLatches = new ArrayMap<>();
309
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700310 private DiskInfo findDiskById(String id) {
311 synchronized (mLock) {
312 final DiskInfo disk = mDisks.get(id);
313 if (disk != null) {
314 return disk;
315 }
316 }
317 throw new IllegalArgumentException("No disk found for ID " + id);
318 }
319
320 private VolumeInfo findVolumeById(String id) {
321 synchronized (mLock) {
322 final VolumeInfo vol = mVolumes.get(id);
323 if (vol != null) {
324 return vol;
325 }
326 }
327 throw new IllegalArgumentException("No volume found for ID " + id);
328 }
329
Jeff Sharkey48877892015-03-18 11:27:19 -0700330 @Deprecated
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700331 private String findVolumeIdForPath(String path) {
Jeff Sharkey48877892015-03-18 11:27:19 -0700332 synchronized (mLock) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700333 for (int i = 0; i < mVolumes.size(); i++) {
334 final VolumeInfo vol = mVolumes.valueAt(i);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700335 if (vol.path != null && path.startsWith(vol.path)) {
336 return vol.id;
Jeff Sharkey48877892015-03-18 11:27:19 -0700337 }
338 }
339 }
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700340 throw new IllegalArgumentException("No volume found for path " + path);
Jeff Sharkey48877892015-03-18 11:27:19 -0700341 }
342
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700343 private VolumeMetadata findOrCreateMetadataLocked(VolumeInfo vol) {
344 VolumeMetadata meta = mMetadata.get(vol.fsUuid);
345 if (meta == null) {
346 meta = new VolumeMetadata(vol.type, vol.fsUuid);
347 mMetadata.put(meta.fsUuid, meta);
348 }
349 return meta;
350 }
351
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700352 private CountDownLatch findOrCreateDiskScanLatch(String diskId) {
353 synchronized (mLock) {
354 CountDownLatch latch = mDiskScanLatches.get(diskId);
355 if (latch == null) {
356 latch = new CountDownLatch(1);
357 mDiskScanLatches.put(diskId, latch);
358 }
359 return latch;
360 }
361 }
362
Jeff Sharkey48877892015-03-18 11:27:19 -0700363 private static int sNextMtpIndex = 1;
364
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700365 private static int allocateMtpIndex(String volId) {
366 if (VolumeInfo.ID_EMULATED_INTERNAL.equals(volId)) {
367 return 0;
368 } else {
369 return sNextMtpIndex++;
Jeff Sharkey48877892015-03-18 11:27:19 -0700370 }
371 }
372
Paul Lawrence8e397362014-01-27 15:22:30 -0800373 /** List of crypto types.
374 * These must match CRYPT_TYPE_XXX in cryptfs.h AND their
375 * corresponding commands in CommandListener.cpp */
376 public static final String[] CRYPTO_TYPES
377 = { "password", "default", "pattern", "pin" };
378
Brian Carlstrom7395a8a2014-04-28 22:11:01 -0700379 private final Context mContext;
Brian Carlstromdfad99a2014-05-07 15:21:14 -0700380 private final NativeDaemonConnector mConnector;
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700381
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700382 private volatile boolean mSystemReady = false;
Jeff Sharkey48877892015-03-18 11:27:19 -0700383 private volatile boolean mDaemonConnected = false;
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700384
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700385 private PackageManagerService mPms;
386
387 private final Callbacks mCallbacks;
Jeff Sharkey48877892015-03-18 11:27:19 -0700388
Jeff Sharkey0be607c2012-11-14 14:39:19 -0800389 private final CountDownLatch mConnectedSignal = new CountDownLatch(1);
390 private final CountDownLatch mAsecsScanned = new CountDownLatch(1);
Jeff Sharkey48877892015-03-18 11:27:19 -0700391
392 private final Object mUnmountLock = new Object();
393 @GuardedBy("mUnmountLock")
394 private CountDownLatch mUnmountSignal;
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -0800395
San Mehat6cdd9c02010-02-09 14:45:20 -0800396 /**
397 * Private hash of currently mounted secure containers.
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800398 * Used as a lock in methods to manipulate secure containers.
San Mehat6cdd9c02010-02-09 14:45:20 -0800399 */
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800400 final private HashSet<String> mAsecMountSet = new HashSet<String>();
San Mehat6cdd9c02010-02-09 14:45:20 -0800401
Kenny Root02c87302010-07-01 08:10:18 -0700402 /**
Kenny Root3b1abba2010-10-13 15:00:07 -0700403 * The size of the crypto algorithm key in bits for OBB files. Currently
404 * Twofish is used which takes 128-bit keys.
405 */
406 private static final int CRYPTO_ALGORITHM_KEY_SIZE = 128;
407
408 /**
409 * The number of times to run SHA1 in the PBKDF2 function for OBB files.
410 * 1024 is reasonably secure and not too slow.
411 */
412 private static final int PBKDF2_HASH_ROUNDS = 1024;
413
414 /**
Kenny Roota02b8b02010-08-05 16:14:17 -0700415 * Mounted OBB tracking information. Used to track the current state of all
416 * OBBs.
Kenny Root02c87302010-07-01 08:10:18 -0700417 */
Kenny Root735de3b2010-09-30 14:11:39 -0700418 final private Map<IBinder, List<ObbState>> mObbMounts = new HashMap<IBinder, List<ObbState>>();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700419
420 /** Map from raw paths to {@link ObbState}. */
Kenny Roota02b8b02010-08-05 16:14:17 -0700421 final private Map<String, ObbState> mObbPathToStateMap = new HashMap<String, ObbState>();
422
423 class ObbState implements IBinder.DeathRecipient {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700424 public ObbState(String rawPath, String canonicalPath, int callingUid,
425 IObbActionListener token, int nonce) {
426 this.rawPath = rawPath;
427 this.canonicalPath = canonicalPath.toString();
428
429 final int userId = UserHandle.getUserId(callingUid);
430 this.ownerPath = buildObbPath(canonicalPath, userId, false);
431 this.voldPath = buildObbPath(canonicalPath, userId, true);
432
433 this.ownerGid = UserHandle.getSharedAppGid(callingUid);
Kenny Rootaf9d6672010-10-08 09:21:39 -0700434 this.token = token;
435 this.nonce = nonce;
Kenny Roota02b8b02010-08-05 16:14:17 -0700436 }
437
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700438 final String rawPath;
439 final String canonicalPath;
440 final String ownerPath;
441 final String voldPath;
Kenny Roota02b8b02010-08-05 16:14:17 -0700442
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700443 final int ownerGid;
Kenny Roota02b8b02010-08-05 16:14:17 -0700444
Kenny Rootaf9d6672010-10-08 09:21:39 -0700445 // Token of remote Binder caller
446 final IObbActionListener token;
447
448 // Identifier to pass back to the token
449 final int nonce;
Kenny Roota02b8b02010-08-05 16:14:17 -0700450
Kenny Root735de3b2010-09-30 14:11:39 -0700451 public IBinder getBinder() {
452 return token.asBinder();
453 }
454
Kenny Roota02b8b02010-08-05 16:14:17 -0700455 @Override
456 public void binderDied() {
457 ObbAction action = new UnmountObbAction(this, true);
458 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
Kenny Root735de3b2010-09-30 14:11:39 -0700459 }
Kenny Roota02b8b02010-08-05 16:14:17 -0700460
Kenny Root5919ac62010-10-05 09:49:40 -0700461 public void link() throws RemoteException {
462 getBinder().linkToDeath(this, 0);
463 }
464
465 public void unlink() {
Kenny Root735de3b2010-09-30 14:11:39 -0700466 getBinder().unlinkToDeath(this, 0);
Kenny Roota02b8b02010-08-05 16:14:17 -0700467 }
Kenny Root38cf8862010-09-26 14:18:51 -0700468
469 @Override
470 public String toString() {
471 StringBuilder sb = new StringBuilder("ObbState{");
Jeff Sharkey4fbbda42012-09-24 18:34:07 -0700472 sb.append("rawPath=").append(rawPath);
473 sb.append(",canonicalPath=").append(canonicalPath);
474 sb.append(",ownerPath=").append(ownerPath);
475 sb.append(",voldPath=").append(voldPath);
476 sb.append(",ownerGid=").append(ownerGid);
477 sb.append(",token=").append(token);
478 sb.append(",binder=").append(getBinder());
Kenny Root38cf8862010-09-26 14:18:51 -0700479 sb.append('}');
480 return sb.toString();
481 }
Kenny Roota02b8b02010-08-05 16:14:17 -0700482 }
483
484 // OBB Action Handler
485 final private ObbActionHandler mObbActionHandler;
486
487 // OBB action handler messages
488 private static final int OBB_RUN_ACTION = 1;
489 private static final int OBB_MCS_BOUND = 2;
490 private static final int OBB_MCS_UNBIND = 3;
491 private static final int OBB_MCS_RECONNECT = 4;
Kenny Rootaf9d6672010-10-08 09:21:39 -0700492 private static final int OBB_FLUSH_MOUNT_STATE = 5;
Kenny Roota02b8b02010-08-05 16:14:17 -0700493
494 /*
495 * Default Container Service information
496 */
497 static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
498 "com.android.defcontainer", "com.android.defcontainer.DefaultContainerService");
499
500 final private DefaultContainerConnection mDefContainerConn = new DefaultContainerConnection();
501
502 class DefaultContainerConnection implements ServiceConnection {
Jeff Sharkey48877892015-03-18 11:27:19 -0700503 @Override
Kenny Roota02b8b02010-08-05 16:14:17 -0700504 public void onServiceConnected(ComponentName name, IBinder service) {
505 if (DEBUG_OBB)
506 Slog.i(TAG, "onServiceConnected");
507 IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(service);
508 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_MCS_BOUND, imcs));
509 }
510
Jeff Sharkey48877892015-03-18 11:27:19 -0700511 @Override
Kenny Roota02b8b02010-08-05 16:14:17 -0700512 public void onServiceDisconnected(ComponentName name) {
513 if (DEBUG_OBB)
514 Slog.i(TAG, "onServiceDisconnected");
515 }
516 };
517
518 // Used in the ObbActionHandler
519 private IMediaContainerService mContainerService = null;
Kenny Root02c87302010-07-01 08:10:18 -0700520
Christopher Tate7265abe2014-11-21 13:54:45 -0800521 // Last fstrim operation tracking
522 private static final String LAST_FSTRIM_FILE = "last-fstrim";
523 private final File mLastMaintenanceFile;
524 private long mLastMaintenance;
525
Kenny Root02c87302010-07-01 08:10:18 -0700526 // Handler messages
Jeff Sharkey48877892015-03-18 11:27:19 -0700527 private static final int H_SYSTEM_READY = 1;
528 private static final int H_DAEMON_CONNECTED = 2;
529 private static final int H_SHUTDOWN = 3;
530 private static final int H_FSTRIM = 4;
531 private static final int H_VOLUME_MOUNT = 5;
532 private static final int H_VOLUME_BROADCAST = 6;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800533
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400534 class MountServiceHandler extends Handler {
Jeff Sharkey48877892015-03-18 11:27:19 -0700535 public MountServiceHandler(Looper looper) {
536 super(looper);
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400537 }
538
Jason parks5af0b912010-11-29 09:05:25 -0600539 @Override
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800540 public void handleMessage(Message msg) {
541 switch (msg.what) {
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700542 case H_SYSTEM_READY: {
Jeff Sharkey48877892015-03-18 11:27:19 -0700543 handleSystemReady();
544 break;
545 }
546 case H_DAEMON_CONNECTED: {
547 handleDaemonConnected();
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700548 break;
549 }
Christopher Tated417d622013-08-19 16:14:25 -0700550 case H_FSTRIM: {
Jeff Sharkey1783f142015-04-17 10:52:51 -0700551 if (!isReady()) {
552 Slog.i(TAG, "fstrim requested, but no daemon connection yet; trying again");
553 sendMessageDelayed(obtainMessage(H_FSTRIM), DateUtils.SECOND_IN_MILLIS);
554 }
555
Christopher Tated417d622013-08-19 16:14:25 -0700556 Slog.i(TAG, "Running fstrim idle maintenance");
Christopher Tate7265abe2014-11-21 13:54:45 -0800557
558 // Remember when we kicked it off
559 try {
560 mLastMaintenance = System.currentTimeMillis();
561 mLastMaintenanceFile.setLastModified(mLastMaintenance);
562 } catch (Exception e) {
563 Slog.e(TAG, "Unable to record last fstrim!");
564 }
565
Christopher Tated417d622013-08-19 16:14:25 -0700566 try {
567 // This method must be run on the main (handler) thread,
568 // so it is safe to directly call into vold.
569 mConnector.execute("fstrim", "dotrim");
570 EventLogTags.writeFstrimStart(SystemClock.elapsedRealtime());
571 } catch (NativeDaemonConnectorException ndce) {
572 Slog.e(TAG, "Failed to run fstrim!");
573 }
Christopher Tate7265abe2014-11-21 13:54:45 -0800574
Christopher Tated417d622013-08-19 16:14:25 -0700575 // invoke the completion callback, if any
576 Runnable callback = (Runnable) msg.obj;
577 if (callback != null) {
578 callback.run();
579 }
580 break;
581 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700582 case H_SHUTDOWN: {
583 final IMountShutdownObserver obs = (IMountShutdownObserver) msg.obj;
584 boolean success = false;
585 try {
586 success = mConnector.execute("volume", "shutdown").isClassOk();
587 } catch (NativeDaemonConnectorException ignored) {
588 }
589 if (obs != null) {
590 try {
591 obs.onShutDownComplete(success ? 0 : -1);
592 } catch (RemoteException ignored) {
593 }
594 }
595 break;
596 }
597 case H_VOLUME_MOUNT: {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700598 final VolumeInfo vol = (VolumeInfo) msg.obj;
Jeff Sharkey48877892015-03-18 11:27:19 -0700599 try {
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700600 mConnector.execute("volume", "mount", vol.id, vol.mountFlags,
601 vol.mountUserId);
Jeff Sharkey48877892015-03-18 11:27:19 -0700602 } catch (NativeDaemonConnectorException ignored) {
603 }
604 break;
605 }
606 case H_VOLUME_BROADCAST: {
607 final StorageVolume userVol = (StorageVolume) msg.obj;
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700608 final String envState = userVol.getState();
609 Slog.d(TAG, "Volume " + userVol.getId() + " broadcasting " + envState + " to "
Jeff Sharkey48877892015-03-18 11:27:19 -0700610 + userVol.getOwner());
611
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700612 final String action = VolumeInfo.getBroadcastForEnvironment(envState);
Jeff Sharkey48877892015-03-18 11:27:19 -0700613 if (action != null) {
614 final Intent intent = new Intent(action,
615 Uri.fromFile(userVol.getPathFile()));
616 intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, userVol);
617 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
618 mContext.sendBroadcastAsUser(intent, userVol.getOwner());
619 }
620 break;
621 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800622 }
623 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700624 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700625
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700626 private final Handler mHandler;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800627
Jeff Sharkey56e62932015-03-21 20:41:00 -0700628 @Override
629 public void waitForAsecScan() {
Jeff Sharkey48877892015-03-18 11:27:19 -0700630 waitForLatch(mAsecsScanned, "mAsecsScanned");
Kenny Root51a573c2012-05-17 13:30:28 -0700631 }
632
San Mehat207e5382010-02-04 20:46:54 -0800633 private void waitForReady() {
Jeff Sharkey48877892015-03-18 11:27:19 -0700634 waitForLatch(mConnectedSignal, "mConnectedSignal");
Kenny Root51a573c2012-05-17 13:30:28 -0700635 }
636
Jeff Sharkey48877892015-03-18 11:27:19 -0700637 private void waitForLatch(CountDownLatch latch, String condition) {
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700638 while (true) {
Kenny Root51a573c2012-05-17 13:30:28 -0700639 try {
640 if (latch.await(5000, TimeUnit.MILLISECONDS)) {
San Mehat207e5382010-02-04 20:46:54 -0800641 return;
Kenny Root51a573c2012-05-17 13:30:28 -0700642 } else {
643 Slog.w(TAG, "Thread " + Thread.currentThread().getName()
Jeff Sharkey48877892015-03-18 11:27:19 -0700644 + " still waiting for " + condition + "...");
San Mehat207e5382010-02-04 20:46:54 -0800645 }
Kenny Root51a573c2012-05-17 13:30:28 -0700646 } catch (InterruptedException e) {
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700647 Slog.w(TAG, "Interrupt while waiting for " + condition);
San Mehat207e5382010-02-04 20:46:54 -0800648 }
San Mehat207e5382010-02-04 20:46:54 -0800649 }
San Mehat1f6301e2010-01-07 22:40:27 -0800650 }
Kenny Root02c87302010-07-01 08:10:18 -0700651
Paul Lawrence945490c2014-03-27 16:37:28 +0000652 private boolean isReady() {
653 try {
654 return mConnectedSignal.await(0, TimeUnit.MILLISECONDS);
655 } catch (InterruptedException e) {
656 return false;
657 }
658 }
659
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700660 private void handleSystemReady() {
Jeff Sharkey48877892015-03-18 11:27:19 -0700661 resetIfReadyAndConnected();
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700662
Jeff Sharkey48877892015-03-18 11:27:19 -0700663 // Start scheduling nominally-daily fstrim operations
Christopher Tate115afda2014-06-06 19:06:26 -0700664 MountServiceIdler.scheduleIdlePass(mContext);
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700665 }
666
Jeff Sharkey48877892015-03-18 11:27:19 -0700667 private void resetIfReadyAndConnected() {
668 Slog.d(TAG, "Thinking about reset, mSystemReady=" + mSystemReady
669 + ", mDaemonConnected=" + mDaemonConnected);
670 if (mSystemReady && mDaemonConnected) {
671 mDisks.clear();
672 mVolumes.clear();
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700673
Jeff Sharkey59d577a2015-04-11 21:27:21 -0700674 // Create a stub volume that represents internal storage
675 final VolumeInfo internal = new VolumeInfo(VolumeInfo.ID_PRIVATE_INTERNAL,
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700676 VolumeInfo.TYPE_PRIVATE, null, 0);
Jeff Sharkey59d577a2015-04-11 21:27:21 -0700677 internal.state = VolumeInfo.STATE_MOUNTED;
678 internal.path = Environment.getDataDirectory().getAbsolutePath();
679 mVolumes.put(internal.id, internal);
680
Jeff Sharkey48877892015-03-18 11:27:19 -0700681 try {
682 mConnector.execute("volume", "reset");
683 } catch (NativeDaemonConnectorException e) {
684 Slog.w(TAG, "Failed to reset vold", e);
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700685 }
686 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700687 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700688
Jeff Sharkey48877892015-03-18 11:27:19 -0700689 private void onStartUser(int userId) {
690 Slog.d(TAG, "onStartUser " + userId);
691
692 // We purposefully block here to make sure that user-specific
693 // staging area is ready so it's ready for zygote-forked apps to
694 // bind mount against.
695 try {
696 mConnector.execute("volume", "start_user", userId);
697 } catch (NativeDaemonConnectorException ignored) {
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700698 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700699
700 // Record user as started so newly mounted volumes kick off events
701 // correctly, then synthesize events for any already-mounted volumes.
702 synchronized (mVolumes) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700703 for (int i = 0; i < mVolumes.size(); i++) {
704 final VolumeInfo vol = mVolumes.valueAt(i);
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700705 if (vol.isVisibleToUser(userId) && vol.isMountedReadable()) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700706 final StorageVolume userVol = vol.buildStorageVolume(mContext, userId);
Jeff Sharkey48877892015-03-18 11:27:19 -0700707 mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget();
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700708
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700709 final String envState = VolumeInfo.getEnvironmentForState(vol.getState());
710 mCallbacks.notifyStorageStateChanged(userVol.getPath(), envState, envState);
Jeff Sharkey48877892015-03-18 11:27:19 -0700711 }
712 }
713 mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userId);
714 }
715 }
716
717 private void onCleanupUser(int userId) {
718 Slog.d(TAG, "onCleanupUser " + userId);
719
720 try {
721 mConnector.execute("volume", "cleanup_user", userId);
722 } catch (NativeDaemonConnectorException ignored) {
723 }
724
725 synchronized (mVolumes) {
726 mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userId);
727 }
728 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700729
Christopher Tated417d622013-08-19 16:14:25 -0700730 void runIdleMaintenance(Runnable callback) {
731 mHandler.sendMessage(mHandler.obtainMessage(H_FSTRIM, callback));
732 }
733
Christopher Tate7265abe2014-11-21 13:54:45 -0800734 // Binder entry point for kicking off an immediate fstrim
735 @Override
736 public void runMaintenance() {
Jeff Sharkey48877892015-03-18 11:27:19 -0700737 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
Christopher Tate7265abe2014-11-21 13:54:45 -0800738 runIdleMaintenance(null);
739 }
740
741 @Override
742 public long lastMaintenance() {
743 return mLastMaintenance;
744 }
745
San Mehat4270e1e2010-01-29 05:32:19 -0800746 /**
San Mehat4270e1e2010-01-29 05:32:19 -0800747 * Callback from NativeDaemonConnector
748 */
Jeff Sharkey48877892015-03-18 11:27:19 -0700749 @Override
San Mehat4270e1e2010-01-29 05:32:19 -0800750 public void onDaemonConnected() {
Jeff Sharkey48877892015-03-18 11:27:19 -0700751 mDaemonConnected = true;
752 mHandler.obtainMessage(H_DAEMON_CONNECTED).sendToTarget();
753 }
754
755 private void handleDaemonConnected() {
756 resetIfReadyAndConnected();
757
San Mehat4270e1e2010-01-29 05:32:19 -0800758 /*
Jeff Sharkey48877892015-03-18 11:27:19 -0700759 * Now that we've done our initialization, release
760 * the hounds!
San Mehat4270e1e2010-01-29 05:32:19 -0800761 */
Jeff Sharkey48877892015-03-18 11:27:19 -0700762 mConnectedSignal.countDown();
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400763
Jeff Sharkey48877892015-03-18 11:27:19 -0700764 // On an encrypted device we can't see system properties yet, so pull
765 // the system locale out of the mount service.
766 if ("".equals(SystemProperties.get("vold.encrypt_progress"))) {
767 copyLocaleFromMountService();
768 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700769
Jeff Sharkey48877892015-03-18 11:27:19 -0700770 // Let package manager load internal ASECs.
771 mPms.scanAvailableAsecs();
Mike Lockwood7fa24aa2011-03-23 14:52:34 -0400772
Jeff Sharkey48877892015-03-18 11:27:19 -0700773 // Notify people waiting for ASECs to be scanned that it's done.
774 mAsecsScanned.countDown();
San Mehat4270e1e2010-01-29 05:32:19 -0800775 }
776
Elliott Hughesf839b4f2014-09-26 12:30:47 -0700777 private void copyLocaleFromMountService() {
778 String systemLocale;
779 try {
780 systemLocale = getField(StorageManager.SYSTEM_LOCALE_KEY);
781 } catch (RemoteException e) {
782 return;
783 }
784 if (TextUtils.isEmpty(systemLocale)) {
785 return;
786 }
787
788 Slog.d(TAG, "Got locale " + systemLocale + " from mount service");
789 Locale locale = Locale.forLanguageTag(systemLocale);
790 Configuration config = new Configuration();
791 config.setLocale(locale);
792 try {
793 ActivityManagerNative.getDefault().updateConfiguration(config);
794 } catch (RemoteException e) {
795 Slog.e(TAG, "Error setting system locale from mount service", e);
796 }
Elliott Hughes9c33f282014-10-13 12:39:56 -0700797
798 // Temporary workaround for http://b/17945169.
799 Slog.d(TAG, "Setting system properties to " + systemLocale + " from mount service");
Narayan Kamathd30dbb82015-01-15 14:48:15 +0000800 SystemProperties.set("persist.sys.locale", locale.toLanguageTag());
Elliott Hughesf839b4f2014-09-26 12:30:47 -0700801 }
802
San Mehat4270e1e2010-01-29 05:32:19 -0800803 /**
San Mehat4270e1e2010-01-29 05:32:19 -0800804 * Callback from NativeDaemonConnector
805 */
Jeff Sharkey48877892015-03-18 11:27:19 -0700806 @Override
Dianne Hackborn77b987f2014-02-26 16:20:52 -0800807 public boolean onCheckHoldWakeLock(int code) {
808 return false;
809 }
810
811 /**
812 * Callback from NativeDaemonConnector
813 */
Jeff Sharkey48877892015-03-18 11:27:19 -0700814 @Override
San Mehat4270e1e2010-01-29 05:32:19 -0800815 public boolean onEvent(int code, String raw, String[] cooked) {
Jeff Sharkey48877892015-03-18 11:27:19 -0700816 synchronized (mLock) {
817 return onEventLocked(code, raw, cooked);
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800818 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700819 }
Jeff Sharkey5aca2b82013-10-16 16:21:54 -0700820
Jeff Sharkey48877892015-03-18 11:27:19 -0700821 private boolean onEventLocked(int code, String raw, String[] cooked) {
822 switch (code) {
823 case VoldResponseCode.DISK_CREATED: {
824 if (cooked.length != 3) break;
825 final String id = cooked[1];
Jeff Sharkey74acbbb2015-04-21 12:14:03 -0700826 int flags = Integer.parseInt(cooked[2]);
827 if (SystemProperties.getBoolean(StorageManager.PROP_FORCE_ADOPTABLE, false)) {
828 flags |= DiskInfo.FLAG_ADOPTABLE;
829 }
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700830 mDisks.put(id, new DiskInfo(id, flags));
Jeff Sharkey48877892015-03-18 11:27:19 -0700831 break;
Jeff Sharkey5aca2b82013-10-16 16:21:54 -0700832 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700833 case VoldResponseCode.DISK_SIZE_CHANGED: {
834 if (cooked.length != 3) break;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700835 final DiskInfo disk = mDisks.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -0700836 if (disk != null) {
837 disk.size = Long.parseLong(cooked[2]);
San Mehat4270e1e2010-01-29 05:32:19 -0800838 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700839 break;
840 }
841 case VoldResponseCode.DISK_LABEL_CHANGED: {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700842 final DiskInfo disk = mDisks.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -0700843 if (disk != null) {
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700844 final StringBuilder builder = new StringBuilder();
845 for (int i = 2; i < cooked.length; i++) {
846 builder.append(cooked[i]).append(' ');
847 }
848 disk.label = builder.toString().trim();
Jeff Sharkey48877892015-03-18 11:27:19 -0700849 }
850 break;
851 }
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700852 case VoldResponseCode.DISK_SCANNED: {
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700853 if (cooked.length != 2) break;
854 final DiskInfo disk = mDisks.get(cooked[1]);
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700855 if (disk != null) {
856 onDiskScannedLocked(disk);
857 }
Jeff Sharkey59d577a2015-04-11 21:27:21 -0700858 break;
859 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700860 case VoldResponseCode.DISK_DESTROYED: {
861 if (cooked.length != 2) break;
862 mDisks.remove(cooked[1]);
863 break;
864 }
San Mehat4270e1e2010-01-29 05:32:19 -0800865
Jeff Sharkey48877892015-03-18 11:27:19 -0700866 case VoldResponseCode.VOLUME_CREATED: {
Jeff Sharkey48877892015-03-18 11:27:19 -0700867 final String id = cooked[1];
868 final int type = Integer.parseInt(cooked[2]);
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700869 final String diskId = (cooked.length == 4) ? cooked[3] : null;
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700870 final DiskInfo disk = mDisks.get(diskId);
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700871 final int mtpIndex = allocateMtpIndex(id);
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700872 final VolumeInfo vol = new VolumeInfo(id, type, disk, mtpIndex);
Jeff Sharkey48877892015-03-18 11:27:19 -0700873 mVolumes.put(id, vol);
874 onVolumeCreatedLocked(vol);
875 break;
876 }
877 case VoldResponseCode.VOLUME_STATE_CHANGED: {
878 if (cooked.length != 3) break;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700879 final VolumeInfo vol = mVolumes.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -0700880 if (vol != null) {
881 final int oldState = vol.state;
882 final int newState = Integer.parseInt(cooked[2]);
883 vol.state = newState;
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700884 onVolumeStateChangedLocked(vol.clone(), oldState, newState);
Jeff Sharkey48877892015-03-18 11:27:19 -0700885 }
886 break;
887 }
888 case VoldResponseCode.VOLUME_FS_TYPE_CHANGED: {
889 if (cooked.length != 3) break;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700890 final VolumeInfo vol = mVolumes.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -0700891 if (vol != null) {
892 vol.fsType = cooked[2];
893 }
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700894 mCallbacks.notifyVolumeMetadataChanged(vol.clone());
Jeff Sharkey48877892015-03-18 11:27:19 -0700895 break;
896 }
897 case VoldResponseCode.VOLUME_FS_UUID_CHANGED: {
898 if (cooked.length != 3) break;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700899 final VolumeInfo vol = mVolumes.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -0700900 if (vol != null) {
901 vol.fsUuid = cooked[2];
902 }
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700903 refreshMetadataLocked();
904 mCallbacks.notifyVolumeMetadataChanged(vol.clone());
Jeff Sharkey48877892015-03-18 11:27:19 -0700905 break;
906 }
907 case VoldResponseCode.VOLUME_FS_LABEL_CHANGED: {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700908 final VolumeInfo vol = mVolumes.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -0700909 if (vol != null) {
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700910 final StringBuilder builder = new StringBuilder();
911 for (int i = 2; i < cooked.length; i++) {
912 builder.append(cooked[i]).append(' ');
913 }
914 vol.fsLabel = builder.toString().trim();
Jeff Sharkey48877892015-03-18 11:27:19 -0700915 }
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700916 mCallbacks.notifyVolumeMetadataChanged(vol.clone());
Jeff Sharkey48877892015-03-18 11:27:19 -0700917 break;
918 }
919 case VoldResponseCode.VOLUME_PATH_CHANGED: {
920 if (cooked.length != 3) break;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700921 final VolumeInfo vol = mVolumes.get(cooked[1]);
Jeff Sharkey48877892015-03-18 11:27:19 -0700922 if (vol != null) {
923 vol.path = cooked[2];
924 }
925 break;
926 }
927 case VoldResponseCode.VOLUME_DESTROYED: {
928 if (cooked.length != 2) break;
929 mVolumes.remove(cooked[1]);
930 break;
931 }
San Mehat4270e1e2010-01-29 05:32:19 -0800932
Jeff Sharkey48877892015-03-18 11:27:19 -0700933 case VoldResponseCode.FstrimCompleted: {
Svetoslav9e814a82013-04-30 10:43:56 -0700934 EventLogTags.writeFstrimFinish(SystemClock.elapsedRealtime());
Jeff Sharkey48877892015-03-18 11:27:19 -0700935 break;
San Mehat4270e1e2010-01-29 05:32:19 -0800936 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700937 default: {
938 Slog.d(TAG, "Unhandled vold event " + code);
Mike Lockwooda5250c92011-05-23 13:44:04 -0400939 }
San Mehat4270e1e2010-01-29 05:32:19 -0800940 }
941
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400942 return true;
San Mehat4270e1e2010-01-29 05:32:19 -0800943 }
944
Jeff Sharkeyeba260d2015-04-19 14:35:16 -0700945 private void onDiskScannedLocked(DiskInfo disk) {
946 final CountDownLatch latch = mDiskScanLatches.remove(disk.id);
947 if (latch != null) {
948 latch.countDown();
949 }
950
951 boolean empty = true;
952 for (int i = 0; i < mVolumes.size(); i++) {
953 final VolumeInfo vol = mVolumes.valueAt(i);
954 if (Objects.equals(disk.id, vol.getDiskId())) {
955 empty = false;
956 }
957 }
958
959 if (empty) {
960 mCallbacks.notifyDiskUnsupported(disk);
961 }
962 }
963
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700964 private void onVolumeCreatedLocked(VolumeInfo vol) {
965 final boolean primaryPhysical = SystemProperties.getBoolean(
966 StorageManager.PROP_PRIMARY_PHYSICAL, false);
Jeff Sharkey59d577a2015-04-11 21:27:21 -0700967 // TODO: enable switching to another emulated primary
968 if (VolumeInfo.ID_EMULATED_INTERNAL.equals(vol.id) && !primaryPhysical) {
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700969 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
970 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
Jeff Sharkey48877892015-03-18 11:27:19 -0700971 mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
Jeff Sharkeyb049e212012-09-07 23:16:01 -0700972
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700973 } else if (vol.type == VolumeInfo.TYPE_PUBLIC) {
Jeff Sharkey48877892015-03-18 11:27:19 -0700974 if (primaryPhysical) {
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700975 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
976 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
San Mehat4270e1e2010-01-29 05:32:19 -0800977 }
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700978
979 // Adoptable public disks are visible to apps, since they meet
980 // public API requirement of being in a stable location.
981 final DiskInfo disk = mDisks.get(vol.getDiskId());
982 if (disk != null && disk.isAdoptable()) {
983 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
984 }
985
986 vol.mountUserId = UserHandle.USER_OWNER;
Jeff Sharkey48877892015-03-18 11:27:19 -0700987 mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
San Mehat4270e1e2010-01-29 05:32:19 -0800988
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -0700989 } else if (vol.type == VolumeInfo.TYPE_PRIVATE) {
990 mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
991
San Mehat4270e1e2010-01-29 05:32:19 -0800992 } else {
Jeff Sharkey48877892015-03-18 11:27:19 -0700993 Slog.d(TAG, "Skipping automatic mounting of " + vol);
San Mehat4270e1e2010-01-29 05:32:19 -0800994 }
995 }
996
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700997 private boolean isBroadcastWorthy(VolumeInfo vol) {
998 switch (vol.getType()) {
999 case VolumeInfo.TYPE_PUBLIC:
1000 case VolumeInfo.TYPE_EMULATED:
1001 break;
1002 default:
1003 return false;
1004 }
1005
1006 switch (vol.getState()) {
1007 case VolumeInfo.STATE_MOUNTED:
1008 case VolumeInfo.STATE_MOUNTED_READ_ONLY:
1009 case VolumeInfo.STATE_EJECTING:
1010 case VolumeInfo.STATE_UNMOUNTED:
1011 break;
1012 default:
1013 return false;
1014 }
1015
1016 return true;
1017 }
1018
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001019 private void onVolumeStateChangedLocked(VolumeInfo vol, int oldState, int newState) {
Jeff Sharkeye6c04f92015-04-18 21:38:05 -07001020 mCallbacks.notifyVolumeStateChanged(vol, oldState, newState);
1021
1022 if (isBroadcastWorthy(vol)) {
1023 final Intent intent = new Intent(VolumeInfo.ACTION_VOLUME_STATE_CHANGED);
1024 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1025 // TODO: require receiver to hold permission
1026 mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1027 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001028
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001029 final String oldStateEnv = VolumeInfo.getEnvironmentForState(oldState);
1030 final String newStateEnv = VolumeInfo.getEnvironmentForState(newState);
Emily Bernier92aa5a22014-07-07 10:11:48 -04001031
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001032 if (!Objects.equals(oldStateEnv, newStateEnv)) {
1033 // Kick state changed event towards all started users. Any users
1034 // started after this point will trigger additional
1035 // user-specific broadcasts.
1036 for (int userId : mStartedUsers) {
1037 if (vol.isVisibleToUser(userId)) {
1038 final StorageVolume userVol = vol.buildStorageVolume(mContext, userId);
1039 mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget();
Jeff Sharkey48877892015-03-18 11:27:19 -07001040
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001041 mCallbacks.notifyStorageStateChanged(userVol.getPath(), oldStateEnv,
1042 newStateEnv);
San Mehat4270e1e2010-01-29 05:32:19 -08001043 }
1044 }
1045 }
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001046
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07001047 if (vol.type == VolumeInfo.TYPE_PUBLIC && vol.state == VolumeInfo.STATE_EJECTING) {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001048 // TODO: this should eventually be handled by new ObbVolume state changes
1049 /*
1050 * Some OBBs might have been unmounted when this volume was
1051 * unmounted, so send a message to the handler to let it know to
1052 * remove those from the list of mounted OBBS.
1053 */
1054 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(
1055 OBB_FLUSH_MOUNT_STATE, vol.path));
1056 }
San Mehat4270e1e2010-01-29 05:32:19 -08001057 }
1058
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001059 /**
1060 * Refresh latest metadata into any currently active {@link VolumeInfo}.
1061 */
1062 private void refreshMetadataLocked() {
1063 final int size = mVolumes.size();
1064 for (int i = 0; i < size; i++) {
1065 final VolumeInfo vol = mVolumes.valueAt(i);
1066 final VolumeMetadata meta = mMetadata.get(vol.fsUuid);
1067
1068 if (meta != null) {
1069 vol.nickname = meta.nickname;
1070 vol.userFlags = meta.userFlags;
1071 } else {
1072 vol.nickname = null;
1073 vol.userFlags = 0;
1074 }
1075 }
1076 }
1077
Jeff Sharkey48877892015-03-18 11:27:19 -07001078 private void enforcePermission(String perm) {
1079 mContext.enforceCallingOrSelfPermission(perm, perm);
Mike Lockwooda5250c92011-05-23 13:44:04 -04001080 }
1081
Jeff Sharkey48877892015-03-18 11:27:19 -07001082 private void enforceUserRestriction(String restriction) {
Emily Bernier92aa5a22014-07-07 10:11:48 -04001083 UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
Jeff Sharkey48877892015-03-18 11:27:19 -07001084 if (um.hasUserRestriction(restriction, Binder.getCallingUserHandle())) {
Emily Bernier92aa5a22014-07-07 10:11:48 -04001085 throw new SecurityException("User has restriction " + restriction);
1086 }
1087 }
1088
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001089 /**
San Mehat207e5382010-02-04 20:46:54 -08001090 * Constructs a new MountService instance
1091 *
1092 * @param context Binder context for this service
1093 */
1094 public MountService(Context context) {
Christopher Tated417d622013-08-19 16:14:25 -07001095 sSelf = this;
1096
San Mehat207e5382010-02-04 20:46:54 -08001097 mContext = context;
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001098 mCallbacks = new Callbacks(FgThread.get().getLooper());
San Mehat207e5382010-02-04 20:46:54 -08001099
San Mehat207e5382010-02-04 20:46:54 -08001100 // XXX: This will go away soon in favor of IMountServiceObserver
1101 mPms = (PackageManagerService) ServiceManager.getService("package");
1102
Dianne Hackbornefa92b22013-05-03 14:11:43 -07001103 HandlerThread hthread = new HandlerThread(TAG);
1104 hthread.start();
1105 mHandler = new MountServiceHandler(hthread.getLooper());
Daniel Sandler5f27ef42010-03-16 15:42:02 -04001106
Kenny Roota02b8b02010-08-05 16:14:17 -07001107 // Add OBB Action Handler to MountService thread.
Dianne Hackborn8d044e82013-04-30 17:24:15 -07001108 mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper());
Kenny Roota02b8b02010-08-05 16:14:17 -07001109
Christopher Tate7265abe2014-11-21 13:54:45 -08001110 // Initialize the last-fstrim tracking if necessary
1111 File dataDir = Environment.getDataDirectory();
1112 File systemDir = new File(dataDir, "system");
1113 mLastMaintenanceFile = new File(systemDir, LAST_FSTRIM_FILE);
1114 if (!mLastMaintenanceFile.exists()) {
1115 // Not setting mLastMaintenance here means that we will force an
1116 // fstrim during reboot following the OTA that installs this code.
1117 try {
1118 (new FileOutputStream(mLastMaintenanceFile)).close();
1119 } catch (IOException e) {
1120 Slog.e(TAG, "Unable to create fstrim record " + mLastMaintenanceFile.getPath());
1121 }
1122 } else {
1123 mLastMaintenance = mLastMaintenanceFile.lastModified();
1124 }
1125
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001126 mMetadataFile = new AtomicFile(
1127 new File(Environment.getSystemSecureDirectory(), "storage.xml"));
1128
1129 synchronized (mLock) {
1130 readMetadataLocked();
1131 }
1132
Marco Nelissenc34ebce2010-02-18 13:39:41 -08001133 /*
Kenny Root305bcbf2010-09-03 07:56:38 -07001134 * Create the connection to vold with a maximum queue of twice the
1135 * amount of containers we'd ever expect to have. This keeps an
1136 * "asec list" from blocking a thread repeatedly.
1137 */
Dianne Hackborn77b987f2014-02-26 16:20:52 -08001138 mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25,
1139 null);
Jeff Sharkey48877892015-03-18 11:27:19 -07001140 mConnector.setDebug(true);
Kenny Root51a573c2012-05-17 13:30:28 -07001141
Kenny Root305bcbf2010-09-03 07:56:38 -07001142 Thread thread = new Thread(mConnector, VOLD_TAG);
San Mehat207e5382010-02-04 20:46:54 -08001143 thread.start();
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -07001144
Kenny Root07714d42011-08-17 17:49:28 -07001145 // Add ourself to the Watchdog monitors if enabled.
1146 if (WATCHDOG_ENABLE) {
1147 Watchdog.getInstance().addMonitor(this);
1148 }
San Mehat207e5382010-02-04 20:46:54 -08001149 }
1150
Jeff Sharkey56e62932015-03-21 20:41:00 -07001151 private void systemReady() {
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001152 mSystemReady = true;
1153 mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget();
1154 }
1155
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001156 private void readMetadataLocked() {
1157 mMetadata.clear();
1158
1159 FileInputStream fis = null;
1160 try {
1161 fis = mMetadataFile.openRead();
1162 final XmlPullParser in = Xml.newPullParser();
1163 in.setInput(fis, null);
1164
1165 int type;
1166 while ((type = in.next()) != END_DOCUMENT) {
1167 if (type == START_TAG) {
1168 final String tag = in.getName();
1169 if (TAG_VOLUME.equals(tag)) {
1170 final VolumeMetadata meta = VolumeMetadata.read(in);
1171 mMetadata.put(meta.fsUuid, meta);
1172 }
1173 }
1174 }
1175 } catch (FileNotFoundException e) {
1176 // Missing metadata is okay, probably first boot
1177 } catch (IOException e) {
1178 Slog.wtf(TAG, "Failed reading metadata", e);
1179 } catch (XmlPullParserException e) {
1180 Slog.wtf(TAG, "Failed reading metadata", e);
1181 } finally {
1182 IoUtils.closeQuietly(fis);
1183 }
1184 }
1185
1186 private void writeMetadataLocked() {
1187 FileOutputStream fos = null;
1188 try {
1189 fos = mMetadataFile.startWrite();
1190
1191 XmlSerializer out = new FastXmlSerializer();
1192 out.setOutput(fos, "utf-8");
1193 out.startDocument(null, true);
1194 out.startTag(null, TAG_VOLUMES);
1195 final int size = mMetadata.size();
1196 for (int i = 0; i < size; i++) {
1197 final VolumeMetadata meta = mMetadata.valueAt(i);
1198 VolumeMetadata.write(out, meta);
1199 }
1200 out.endTag(null, TAG_VOLUMES);
1201 out.endDocument();
1202
1203 mMetadataFile.finishWrite(fos);
1204 } catch (IOException e) {
1205 if (fos != null) {
1206 mMetadataFile.failWrite(fos);
1207 }
1208 }
1209 }
1210
San Mehat207e5382010-02-04 20:46:54 -08001211 /**
San Mehat4270e1e2010-01-29 05:32:19 -08001212 * Exposed API calls below here
1213 */
1214
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001215 @Override
San Mehat4270e1e2010-01-29 05:32:19 -08001216 public void registerListener(IMountServiceListener listener) {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001217 mCallbacks.register(listener);
San Mehat4270e1e2010-01-29 05:32:19 -08001218 }
1219
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001220 @Override
San Mehat4270e1e2010-01-29 05:32:19 -08001221 public void unregisterListener(IMountServiceListener listener) {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001222 mCallbacks.unregister(listener);
San Mehat4270e1e2010-01-29 05:32:19 -08001223 }
1224
Jeff Sharkey48877892015-03-18 11:27:19 -07001225 @Override
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -08001226 public void shutdown(final IMountShutdownObserver observer) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001227 enforcePermission(android.Manifest.permission.SHUTDOWN);
San Mehat4270e1e2010-01-29 05:32:19 -08001228
San Mehata5078592010-03-25 09:36:54 -07001229 Slog.i(TAG, "Shutting down");
Jeff Sharkey48877892015-03-18 11:27:19 -07001230 mHandler.obtainMessage(H_SHUTDOWN, observer).sendToTarget();
San Mehat4270e1e2010-01-29 05:32:19 -08001231 }
1232
Jeff Sharkey48877892015-03-18 11:27:19 -07001233 @Override
San Mehatb1043402010-02-05 08:26:50 -08001234 public boolean isUsbMassStorageConnected() {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001235 throw new UnsupportedOperationException();
San Mehatb1043402010-02-05 08:26:50 -08001236 }
1237
Jeff Sharkey48877892015-03-18 11:27:19 -07001238 @Override
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001239 public void setUsbMassStorageEnabled(boolean enable) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001240 throw new UnsupportedOperationException();
San Mehatb1043402010-02-05 08:26:50 -08001241 }
1242
Jeff Sharkey48877892015-03-18 11:27:19 -07001243 @Override
San Mehatb1043402010-02-05 08:26:50 -08001244 public boolean isUsbMassStorageEnabled() {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001245 throw new UnsupportedOperationException();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001246 }
Jason parks9ed98bc2011-01-17 09:58:35 -06001247
Jeff Sharkey48877892015-03-18 11:27:19 -07001248 @Override
San Mehat4270e1e2010-01-29 05:32:19 -08001249 public String getVolumeState(String mountPoint) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001250 throw new UnsupportedOperationException();
San Mehat7fd0fee2009-12-17 07:12:23 -08001251 }
1252
Jeff Sharkeyb049e212012-09-07 23:16:01 -07001253 @Override
Kenny Roote1ff2142010-10-12 11:20:01 -07001254 public boolean isExternalStorageEmulated() {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001255 throw new UnsupportedOperationException();
Kenny Roote1ff2142010-10-12 11:20:01 -07001256 }
1257
Jeff Sharkey48877892015-03-18 11:27:19 -07001258 @Override
San Mehat4270e1e2010-01-29 05:32:19 -08001259 public int mountVolume(String path) {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001260 mount(findVolumeIdForPath(path));
1261 return 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001262 }
1263
Jeff Sharkey48877892015-03-18 11:27:19 -07001264 @Override
Ben Komalo13c71972011-09-07 16:35:56 -07001265 public void unmountVolume(String path, boolean force, boolean removeEncryption) {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001266 unmount(findVolumeIdForPath(path));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001267 }
1268
Jeff Sharkey48877892015-03-18 11:27:19 -07001269 @Override
San Mehat4270e1e2010-01-29 05:32:19 -08001270 public int formatVolume(String path) {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001271 format(findVolumeIdForPath(path));
1272 return 0;
1273 }
1274
1275 @Override
1276 public void mount(String volId) {
1277 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1278 waitForReady();
1279
1280 final VolumeInfo vol = findVolumeById(volId);
1281 if (vol.type == VolumeInfo.TYPE_PUBLIC || vol.type == VolumeInfo.TYPE_PRIVATE) {
1282 enforceUserRestriction(UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA);
1283 }
1284 try {
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07001285 mConnector.execute("volume", "mount", vol.id, vol.mountFlags, vol.mountUserId);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001286 } catch (NativeDaemonConnectorException e) {
1287 throw e.rethrowAsParcelableException();
1288 }
1289 }
1290
1291 @Override
1292 public void unmount(String volId) {
1293 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1294 waitForReady();
1295
1296 final VolumeInfo vol = findVolumeById(volId);
1297
1298 // TODO: expand PMS to know about multiple volumes
1299 if (vol.isPrimary()) {
1300 synchronized (mUnmountLock) {
1301 mUnmountSignal = new CountDownLatch(1);
1302 mPms.updateExternalMediaStatus(false, true);
1303 waitForLatch(mUnmountSignal, "mUnmountSignal");
1304 mUnmountSignal = null;
1305 }
1306 }
1307
1308 try {
1309 mConnector.execute("volume", "unmount", vol.id);
1310 } catch (NativeDaemonConnectorException e) {
1311 throw e.rethrowAsParcelableException();
1312 }
1313 }
1314
1315 @Override
1316 public void format(String volId) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001317 enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
San Mehat207e5382010-02-04 20:46:54 -08001318 waitForReady();
San Mehat5b77dab2010-01-26 13:28:50 -08001319
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001320 final VolumeInfo vol = findVolumeById(volId);
1321 try {
1322 mConnector.execute("volume", "format", vol.id);
1323 } catch (NativeDaemonConnectorException e) {
1324 throw e.rethrowAsParcelableException();
Jeff Sharkey48877892015-03-18 11:27:19 -07001325 }
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001326 }
1327
1328 @Override
1329 public void partitionPublic(String diskId) {
1330 enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
1331 waitForReady();
1332
Jeff Sharkeyeba260d2015-04-19 14:35:16 -07001333 final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001334 try {
1335 mConnector.execute("volume", "partition", diskId, "public");
1336 } catch (NativeDaemonConnectorException e) {
1337 throw e.rethrowAsParcelableException();
1338 }
Jeff Sharkeyeba260d2015-04-19 14:35:16 -07001339 waitForLatch(latch, "partitionPublic");
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001340 }
1341
1342 @Override
1343 public void partitionPrivate(String diskId) {
1344 enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
1345 waitForReady();
1346
Jeff Sharkeyeba260d2015-04-19 14:35:16 -07001347 final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001348 try {
1349 mConnector.execute("volume", "partition", diskId, "private");
1350 } catch (NativeDaemonConnectorException e) {
1351 throw e.rethrowAsParcelableException();
1352 }
Jeff Sharkeyeba260d2015-04-19 14:35:16 -07001353 waitForLatch(latch, "partitionPrivate");
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001354 }
1355
1356 @Override
1357 public void partitionMixed(String diskId, int ratio) {
1358 enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
1359 waitForReady();
1360
Jeff Sharkeyeba260d2015-04-19 14:35:16 -07001361 final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07001362 try {
1363 mConnector.execute("volume", "partition", diskId, "mixed", ratio);
1364 } catch (NativeDaemonConnectorException e) {
1365 throw e.rethrowAsParcelableException();
1366 }
Jeff Sharkeyeba260d2015-04-19 14:35:16 -07001367 waitForLatch(latch, "partitionMixed");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001368 }
1369
Jeff Sharkey48877892015-03-18 11:27:19 -07001370 @Override
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07001371 public void setVolumeNickname(String volId, String nickname) {
1372 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1373 waitForReady();
1374
1375 synchronized (mLock) {
1376 final VolumeInfo vol = findVolumeById(volId);
1377 final VolumeMetadata meta = findOrCreateMetadataLocked(vol);
1378 meta.nickname = nickname;
1379 refreshMetadataLocked();
1380 writeMetadataLocked();
1381 mCallbacks.notifyVolumeMetadataChanged(vol.clone());
1382 }
1383 }
1384
1385 @Override
1386 public void setVolumeUserFlags(String volId, int flags, int mask) {
1387 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1388 waitForReady();
1389
1390 synchronized (mLock) {
1391 final VolumeInfo vol = findVolumeById(volId);
1392 final VolumeMetadata meta = findOrCreateMetadataLocked(vol);
1393 meta.userFlags = (meta.userFlags & ~mask) | (flags & mask);
1394 refreshMetadataLocked();
1395 writeMetadataLocked();
1396 mCallbacks.notifyVolumeMetadataChanged(vol.clone());
1397 }
1398 }
1399
1400 @Override
Mike Lockwoodecedfdc2011-06-08 15:11:59 -07001401 public int[] getStorageUsers(String path) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001402 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
San Mehatc1b4ce92010-02-16 17:13:03 -08001403 waitForReady();
1404 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001405 final String[] r = NativeDaemonEvent.filterMessageList(
1406 mConnector.executeForList("storage", "users", path),
1407 VoldResponseCode.StorageUsersListResult);
1408
San Mehatc1b4ce92010-02-16 17:13:03 -08001409 // FMT: <pid> <process name>
1410 int[] data = new int[r.length];
1411 for (int i = 0; i < r.length; i++) {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001412 String[] tok = r[i].split(" ");
San Mehatc1b4ce92010-02-16 17:13:03 -08001413 try {
1414 data[i] = Integer.parseInt(tok[0]);
1415 } catch (NumberFormatException nfe) {
San Mehata5078592010-03-25 09:36:54 -07001416 Slog.e(TAG, String.format("Error parsing pid %s", tok[0]));
San Mehatc1b4ce92010-02-16 17:13:03 -08001417 return new int[0];
1418 }
1419 }
1420 return data;
1421 } catch (NativeDaemonConnectorException e) {
San Mehata5078592010-03-25 09:36:54 -07001422 Slog.e(TAG, "Failed to retrieve storage users list", e);
San Mehatc1b4ce92010-02-16 17:13:03 -08001423 return new int[0];
1424 }
1425 }
1426
San Mehatb1043402010-02-05 08:26:50 -08001427 private void warnOnNotMounted() {
Jeff Sharkey48877892015-03-18 11:27:19 -07001428 synchronized (mLock) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001429 for (int i = 0; i < mVolumes.size(); i++) {
1430 final VolumeInfo vol = mVolumes.valueAt(i);
Jeff Sharkey27de30d2015-04-18 16:20:27 -07001431 if (vol.isPrimary() && vol.isMountedWritable()) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001432 // Cool beans, we have a mounted primary volume
1433 return;
1434 }
Jeff Sharkey32ee8312012-09-30 13:21:31 -07001435 }
San Mehatb1043402010-02-05 08:26:50 -08001436 }
Jeff Sharkey48877892015-03-18 11:27:19 -07001437
1438 Slog.w(TAG, "No primary storage mounted!");
San Mehatb1043402010-02-05 08:26:50 -08001439 }
1440
San Mehat4270e1e2010-01-29 05:32:19 -08001441 public String[] getSecureContainerList() {
Jeff Sharkey48877892015-03-18 11:27:19 -07001442 enforcePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat207e5382010-02-04 20:46:54 -08001443 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001444 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001445
San Mehat4270e1e2010-01-29 05:32:19 -08001446 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001447 return NativeDaemonEvent.filterMessageList(
1448 mConnector.executeForList("asec", "list"), VoldResponseCode.AsecListResult);
San Mehat4270e1e2010-01-29 05:32:19 -08001449 } catch (NativeDaemonConnectorException e) {
1450 return new String[0];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001451 }
1452 }
San Mehat36972292010-01-06 11:06:32 -08001453
Kenny Root6dceb882012-04-12 14:23:49 -07001454 public int createSecureContainer(String id, int sizeMb, String fstype, String key,
1455 int ownerUid, boolean external) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001456 enforcePermission(android.Manifest.permission.ASEC_CREATE);
San Mehat207e5382010-02-04 20:46:54 -08001457 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001458 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001459
San Mehatb1043402010-02-05 08:26:50 -08001460 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001461 try {
Jeff Sharkey56cd6462013-06-07 15:09:15 -07001462 mConnector.execute("asec", "create", id, sizeMb, fstype, new SensitiveArg(key),
1463 ownerUid, external ? "1" : "0");
San Mehat4270e1e2010-01-29 05:32:19 -08001464 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001465 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001466 }
San Mehata181b212010-02-11 06:50:20 -08001467
1468 if (rc == StorageResultCode.OperationSucceeded) {
1469 synchronized (mAsecMountSet) {
1470 mAsecMountSet.add(id);
1471 }
1472 }
San Mehat4270e1e2010-01-29 05:32:19 -08001473 return rc;
San Mehat36972292010-01-06 11:06:32 -08001474 }
1475
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001476 @Override
1477 public int resizeSecureContainer(String id, int sizeMb, String key) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001478 enforcePermission(android.Manifest.permission.ASEC_CREATE);
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001479 waitForReady();
1480 warnOnNotMounted();
1481
1482 int rc = StorageResultCode.OperationSucceeded;
1483 try {
1484 mConnector.execute("asec", "resize", id, sizeMb, new SensitiveArg(key));
1485 } catch (NativeDaemonConnectorException e) {
1486 rc = StorageResultCode.OperationFailedInternalError;
1487 }
1488 return rc;
1489 }
1490
San Mehat4270e1e2010-01-29 05:32:19 -08001491 public int finalizeSecureContainer(String id) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001492 enforcePermission(android.Manifest.permission.ASEC_CREATE);
San Mehatb1043402010-02-05 08:26:50 -08001493 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001494
San Mehatb1043402010-02-05 08:26:50 -08001495 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001496 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001497 mConnector.execute("asec", "finalize", id);
San Mehata181b212010-02-11 06:50:20 -08001498 /*
1499 * Finalization does a remount, so no need
1500 * to update mAsecMountSet
1501 */
San Mehat4270e1e2010-01-29 05:32:19 -08001502 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001503 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001504 }
San Mehat4270e1e2010-01-29 05:32:19 -08001505 return rc;
San Mehat36972292010-01-06 11:06:32 -08001506 }
1507
Kenny Root6dceb882012-04-12 14:23:49 -07001508 public int fixPermissionsSecureContainer(String id, int gid, String filename) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001509 enforcePermission(android.Manifest.permission.ASEC_CREATE);
Kenny Root6dceb882012-04-12 14:23:49 -07001510 warnOnNotMounted();
1511
1512 int rc = StorageResultCode.OperationSucceeded;
1513 try {
1514 mConnector.execute("asec", "fixperms", id, gid, filename);
1515 /*
1516 * Fix permissions does a remount, so no need to update
1517 * mAsecMountSet
1518 */
1519 } catch (NativeDaemonConnectorException e) {
1520 rc = StorageResultCode.OperationFailedInternalError;
1521 }
1522 return rc;
1523 }
1524
San Mehatd9709982010-02-18 11:43:03 -08001525 public int destroySecureContainer(String id, boolean force) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001526 enforcePermission(android.Manifest.permission.ASEC_DESTROY);
San Mehat207e5382010-02-04 20:46:54 -08001527 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001528 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001529
Kenny Rootaa485402010-09-14 14:49:41 -07001530 /*
1531 * Force a GC to make sure AssetManagers in other threads of the
1532 * system_server are cleaned up. We have to do this since AssetManager
1533 * instances are kept as a WeakReference and it's possible we have files
1534 * open on the external storage.
1535 */
1536 Runtime.getRuntime().gc();
1537
San Mehatb1043402010-02-05 08:26:50 -08001538 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001539 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001540 final Command cmd = new Command("asec", "destroy", id);
1541 if (force) {
1542 cmd.appendArg("force");
1543 }
1544 mConnector.execute(cmd);
San Mehat4270e1e2010-01-29 05:32:19 -08001545 } catch (NativeDaemonConnectorException e) {
San Mehatd9709982010-02-18 11:43:03 -08001546 int code = e.getCode();
1547 if (code == VoldResponseCode.OpFailedStorageBusy) {
1548 rc = StorageResultCode.OperationFailedStorageBusy;
1549 } else {
1550 rc = StorageResultCode.OperationFailedInternalError;
1551 }
San Mehat02735bc2010-01-26 15:18:08 -08001552 }
San Mehata181b212010-02-11 06:50:20 -08001553
1554 if (rc == StorageResultCode.OperationSucceeded) {
1555 synchronized (mAsecMountSet) {
1556 if (mAsecMountSet.contains(id)) {
1557 mAsecMountSet.remove(id);
1558 }
1559 }
1560 }
1561
San Mehat4270e1e2010-01-29 05:32:19 -08001562 return rc;
San Mehat36972292010-01-06 11:06:32 -08001563 }
Jason parks9ed98bc2011-01-17 09:58:35 -06001564
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001565 public int mountSecureContainer(String id, String key, int ownerUid, boolean readOnly) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001566 enforcePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
San Mehat207e5382010-02-04 20:46:54 -08001567 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001568 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001569
San Mehata181b212010-02-11 06:50:20 -08001570 synchronized (mAsecMountSet) {
1571 if (mAsecMountSet.contains(id)) {
1572 return StorageResultCode.OperationFailedStorageMounted;
1573 }
1574 }
1575
San Mehatb1043402010-02-05 08:26:50 -08001576 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001577 try {
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001578 mConnector.execute("asec", "mount", id, new SensitiveArg(key), ownerUid,
1579 readOnly ? "ro" : "rw");
San Mehat4270e1e2010-01-29 05:32:19 -08001580 } catch (NativeDaemonConnectorException e) {
Kenny Rootf0304622010-03-19 19:20:42 -07001581 int code = e.getCode();
1582 if (code != VoldResponseCode.OpFailedStorageBusy) {
1583 rc = StorageResultCode.OperationFailedInternalError;
1584 }
San Mehat02735bc2010-01-26 15:18:08 -08001585 }
San Mehat6cdd9c02010-02-09 14:45:20 -08001586
1587 if (rc == StorageResultCode.OperationSucceeded) {
1588 synchronized (mAsecMountSet) {
1589 mAsecMountSet.add(id);
1590 }
1591 }
San Mehat4270e1e2010-01-29 05:32:19 -08001592 return rc;
San Mehat36972292010-01-06 11:06:32 -08001593 }
1594
San Mehatd9709982010-02-18 11:43:03 -08001595 public int unmountSecureContainer(String id, boolean force) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001596 enforcePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
San Mehat207e5382010-02-04 20:46:54 -08001597 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001598 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001599
San Mehat6cdd9c02010-02-09 14:45:20 -08001600 synchronized (mAsecMountSet) {
1601 if (!mAsecMountSet.contains(id)) {
San Mehata181b212010-02-11 06:50:20 -08001602 return StorageResultCode.OperationFailedStorageNotMounted;
San Mehat6cdd9c02010-02-09 14:45:20 -08001603 }
1604 }
1605
Kenny Rootaa485402010-09-14 14:49:41 -07001606 /*
1607 * Force a GC to make sure AssetManagers in other threads of the
1608 * system_server are cleaned up. We have to do this since AssetManager
1609 * instances are kept as a WeakReference and it's possible we have files
1610 * open on the external storage.
1611 */
1612 Runtime.getRuntime().gc();
1613
San Mehatb1043402010-02-05 08:26:50 -08001614 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001615 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001616 final Command cmd = new Command("asec", "unmount", id);
1617 if (force) {
1618 cmd.appendArg("force");
1619 }
1620 mConnector.execute(cmd);
San Mehat4270e1e2010-01-29 05:32:19 -08001621 } catch (NativeDaemonConnectorException e) {
San Mehatd9709982010-02-18 11:43:03 -08001622 int code = e.getCode();
1623 if (code == VoldResponseCode.OpFailedStorageBusy) {
1624 rc = StorageResultCode.OperationFailedStorageBusy;
1625 } else {
1626 rc = StorageResultCode.OperationFailedInternalError;
1627 }
San Mehat02735bc2010-01-26 15:18:08 -08001628 }
San Mehat6cdd9c02010-02-09 14:45:20 -08001629
1630 if (rc == StorageResultCode.OperationSucceeded) {
1631 synchronized (mAsecMountSet) {
1632 mAsecMountSet.remove(id);
1633 }
1634 }
San Mehat4270e1e2010-01-29 05:32:19 -08001635 return rc;
San Mehat9dba7092010-01-18 06:47:41 -08001636 }
1637
San Mehat6cdd9c02010-02-09 14:45:20 -08001638 public boolean isSecureContainerMounted(String id) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001639 enforcePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat6cdd9c02010-02-09 14:45:20 -08001640 waitForReady();
1641 warnOnNotMounted();
1642
1643 synchronized (mAsecMountSet) {
1644 return mAsecMountSet.contains(id);
1645 }
1646 }
1647
San Mehat4270e1e2010-01-29 05:32:19 -08001648 public int renameSecureContainer(String oldId, String newId) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001649 enforcePermission(android.Manifest.permission.ASEC_RENAME);
San Mehat207e5382010-02-04 20:46:54 -08001650 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001651 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001652
San Mehata181b212010-02-11 06:50:20 -08001653 synchronized (mAsecMountSet) {
San Mehat85451ee2010-02-24 08:54:18 -08001654 /*
Jason parks9ed98bc2011-01-17 09:58:35 -06001655 * Because a mounted container has active internal state which cannot be
San Mehat85451ee2010-02-24 08:54:18 -08001656 * changed while active, we must ensure both ids are not currently mounted.
1657 */
1658 if (mAsecMountSet.contains(oldId) || mAsecMountSet.contains(newId)) {
San Mehata181b212010-02-11 06:50:20 -08001659 return StorageResultCode.OperationFailedStorageMounted;
1660 }
1661 }
1662
San Mehatb1043402010-02-05 08:26:50 -08001663 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001664 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001665 mConnector.execute("asec", "rename", oldId, newId);
San Mehat4270e1e2010-01-29 05:32:19 -08001666 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001667 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001668 }
San Mehata181b212010-02-11 06:50:20 -08001669
San Mehat4270e1e2010-01-29 05:32:19 -08001670 return rc;
San Mehat45f61042010-01-23 08:12:43 -08001671 }
1672
San Mehat4270e1e2010-01-29 05:32:19 -08001673 public String getSecureContainerPath(String id) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001674 enforcePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat207e5382010-02-04 20:46:54 -08001675 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001676 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001677
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001678 final NativeDaemonEvent event;
San Mehat2d66cef2010-03-23 11:12:52 -07001679 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001680 event = mConnector.execute("asec", "path", id);
1681 event.checkCode(VoldResponseCode.AsecPathResult);
1682 return event.getMessage();
San Mehat2d66cef2010-03-23 11:12:52 -07001683 } catch (NativeDaemonConnectorException e) {
1684 int code = e.getCode();
1685 if (code == VoldResponseCode.OpFailedStorageNotFound) {
Fredrik Helmera20c8ef2011-02-09 16:16:10 +01001686 Slog.i(TAG, String.format("Container '%s' not found", id));
1687 return null;
San Mehat22dd86e2010-01-12 12:21:18 -08001688 } else {
San Mehat2d66cef2010-03-23 11:12:52 -07001689 throw new IllegalStateException(String.format("Unexpected response code %d", code));
San Mehat22dd86e2010-01-12 12:21:18 -08001690 }
1691 }
San Mehat22dd86e2010-01-12 12:21:18 -08001692 }
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -07001693
Dianne Hackborn292f8bc2011-06-27 16:27:41 -07001694 public String getSecureContainerFilesystemPath(String id) {
Jeff Sharkey48877892015-03-18 11:27:19 -07001695 enforcePermission(android.Manifest.permission.ASEC_ACCESS);
Dianne Hackborn292f8bc2011-06-27 16:27:41 -07001696 waitForReady();
1697 warnOnNotMounted();
1698
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001699 final NativeDaemonEvent event;
Dianne Hackborn292f8bc2011-06-27 16:27:41 -07001700 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001701 event = mConnector.execute("asec", "fspath", id);
1702 event.checkCode(VoldResponseCode.AsecPathResult);
1703 return event.getMessage();
Dianne Hackborn292f8bc2011-06-27 16:27:41 -07001704 } catch (NativeDaemonConnectorException e) {
1705 int code = e.getCode();
1706 if (code == VoldResponseCode.OpFailedStorageNotFound) {
1707 Slog.i(TAG, String.format("Container '%s' not found", id));
1708 return null;
1709 } else {
1710 throw new IllegalStateException(String.format("Unexpected response code %d", code));
1711 }
1712 }
1713 }
1714
Jeff Sharkey48877892015-03-18 11:27:19 -07001715 @Override
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -07001716 public void finishMediaUpdate() {
Jeff Sharkey48877892015-03-18 11:27:19 -07001717 if (mUnmountSignal != null) {
1718 mUnmountSignal.countDown();
1719 } else {
1720 Slog.w(TAG, "Odd, nobody asked to unmount?");
1721 }
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -07001722 }
Kenny Root02c87302010-07-01 08:10:18 -07001723
Kenny Roota02b8b02010-08-05 16:14:17 -07001724 private boolean isUidOwnerOfPackageOrSystem(String packageName, int callerUid) {
1725 if (callerUid == android.os.Process.SYSTEM_UID) {
1726 return true;
1727 }
1728
Kenny Root02c87302010-07-01 08:10:18 -07001729 if (packageName == null) {
1730 return false;
1731 }
1732
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07001733 final int packageUid = mPms.getPackageUid(packageName, UserHandle.getUserId(callerUid));
Kenny Root02c87302010-07-01 08:10:18 -07001734
1735 if (DEBUG_OBB) {
1736 Slog.d(TAG, "packageName = " + packageName + ", packageUid = " +
1737 packageUid + ", callerUid = " + callerUid);
1738 }
1739
1740 return callerUid == packageUid;
1741 }
1742
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001743 public String getMountedObbPath(String rawPath) {
1744 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
Kenny Rootaf9d6672010-10-08 09:21:39 -07001745
Kenny Root02c87302010-07-01 08:10:18 -07001746 waitForReady();
1747 warnOnNotMounted();
1748
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001749 final ObbState state;
1750 synchronized (mObbPathToStateMap) {
1751 state = mObbPathToStateMap.get(rawPath);
1752 }
1753 if (state == null) {
1754 Slog.w(TAG, "Failed to find OBB mounted at " + rawPath);
1755 return null;
1756 }
1757
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001758 final NativeDaemonEvent event;
Kenny Root02c87302010-07-01 08:10:18 -07001759 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001760 event = mConnector.execute("obb", "path", state.voldPath);
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001761 event.checkCode(VoldResponseCode.AsecPathResult);
1762 return event.getMessage();
Kenny Root02c87302010-07-01 08:10:18 -07001763 } catch (NativeDaemonConnectorException e) {
1764 int code = e.getCode();
1765 if (code == VoldResponseCode.OpFailedStorageNotFound) {
Kenny Roota02b8b02010-08-05 16:14:17 -07001766 return null;
Kenny Root02c87302010-07-01 08:10:18 -07001767 } else {
1768 throw new IllegalStateException(String.format("Unexpected response code %d", code));
1769 }
1770 }
1771 }
1772
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001773 @Override
1774 public boolean isObbMounted(String rawPath) {
1775 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
Kenny Rootaf9d6672010-10-08 09:21:39 -07001776 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001777 return mObbPathToStateMap.containsKey(rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07001778 }
Kenny Root02c87302010-07-01 08:10:18 -07001779 }
1780
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001781 @Override
1782 public void mountObb(
1783 String rawPath, String canonicalPath, String key, IObbActionListener token, int nonce) {
1784 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
1785 Preconditions.checkNotNull(canonicalPath, "canonicalPath cannot be null");
1786 Preconditions.checkNotNull(token, "token cannot be null");
Kenny Rootaf9d6672010-10-08 09:21:39 -07001787
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001788 final int callingUid = Binder.getCallingUid();
1789 final ObbState obbState = new ObbState(rawPath, canonicalPath, callingUid, token, nonce);
1790 final ObbAction action = new MountObbAction(obbState, key, callingUid);
Kenny Roota02b8b02010-08-05 16:14:17 -07001791 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
1792
1793 if (DEBUG_OBB)
1794 Slog.i(TAG, "Send to OBB handler: " + action.toString());
Kenny Root02c87302010-07-01 08:10:18 -07001795 }
1796
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001797 @Override
1798 public void unmountObb(String rawPath, boolean force, IObbActionListener token, int nonce) {
1799 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
1800
1801 final ObbState existingState;
1802 synchronized (mObbPathToStateMap) {
1803 existingState = mObbPathToStateMap.get(rawPath);
Kenny Rootf1121dc2010-09-29 07:30:53 -07001804 }
1805
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001806 if (existingState != null) {
1807 // TODO: separate state object from request data
1808 final int callingUid = Binder.getCallingUid();
1809 final ObbState newState = new ObbState(
1810 rawPath, existingState.canonicalPath, callingUid, token, nonce);
1811 final ObbAction action = new UnmountObbAction(newState, force);
1812 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
Kenny Root02c87302010-07-01 08:10:18 -07001813
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07001814 if (DEBUG_OBB)
1815 Slog.i(TAG, "Send to OBB handler: " + action.toString());
1816 } else {
1817 Slog.w(TAG, "Unknown OBB mount at " + rawPath);
1818 }
Kenny Roota02b8b02010-08-05 16:14:17 -07001819 }
1820
Ben Komalo444eca22011-09-01 15:17:44 -07001821 @Override
1822 public int getEncryptionState() {
1823 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
1824 "no permission to access the crypt keeper");
1825
1826 waitForReady();
1827
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001828 final NativeDaemonEvent event;
Ben Komalo444eca22011-09-01 15:17:44 -07001829 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001830 event = mConnector.execute("cryptfs", "cryptocomplete");
1831 return Integer.parseInt(event.getMessage());
Ben Komalo444eca22011-09-01 15:17:44 -07001832 } catch (NumberFormatException e) {
1833 // Bad result - unexpected.
1834 Slog.w(TAG, "Unable to parse result from cryptfs cryptocomplete");
1835 return ENCRYPTION_STATE_ERROR_UNKNOWN;
1836 } catch (NativeDaemonConnectorException e) {
1837 // Something bad happened.
1838 Slog.w(TAG, "Error in communicating with cryptfs in validating");
1839 return ENCRYPTION_STATE_ERROR_UNKNOWN;
1840 }
1841 }
1842
Narayan Kamath1653b1d2014-12-17 13:40:36 +00001843 private static String toHex(String password) {
Paul Lawrence8e397362014-01-27 15:22:30 -08001844 if (password == null) {
Narayan Kamath78108a32014-12-16 12:56:23 +00001845 return "";
Paul Lawrence8e397362014-01-27 15:22:30 -08001846 }
1847 byte[] bytes = password.getBytes(StandardCharsets.UTF_8);
Narayan Kamath78108a32014-12-16 12:56:23 +00001848 return new String(HexEncoding.encode(bytes));
Paul Lawrence8e397362014-01-27 15:22:30 -08001849 }
1850
Narayan Kamath1653b1d2014-12-17 13:40:36 +00001851 private static String fromHex(String hexPassword) throws IllegalArgumentException {
Paul Lawrence945490c2014-03-27 16:37:28 +00001852 if (hexPassword == null) {
1853 return null;
1854 }
1855
Narayan Kamath1653b1d2014-12-17 13:40:36 +00001856 final byte[] bytes = HexEncoding.decode(hexPassword.toCharArray(), false);
Narayan Kamath78108a32014-12-16 12:56:23 +00001857 return new String(bytes, StandardCharsets.UTF_8);
Paul Lawrence945490c2014-03-27 16:37:28 +00001858 }
1859
Ben Komalo444eca22011-09-01 15:17:44 -07001860 @Override
Jason parks5af0b912010-11-29 09:05:25 -06001861 public int decryptStorage(String password) {
Jason parksf7b3cd42011-01-27 09:28:25 -06001862 if (TextUtils.isEmpty(password)) {
1863 throw new IllegalArgumentException("password cannot be empty");
Jason parks5af0b912010-11-29 09:05:25 -06001864 }
1865
Jason parks8888c592011-01-20 22:46:41 -06001866 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
1867 "no permission to access the crypt keeper");
Jason parks5af0b912010-11-29 09:05:25 -06001868
1869 waitForReady();
1870
1871 if (DEBUG_EVENTS) {
1872 Slog.i(TAG, "decrypting storage...");
1873 }
1874
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001875 final NativeDaemonEvent event;
Jason parks5af0b912010-11-29 09:05:25 -06001876 try {
Paul Lawrence8e397362014-01-27 15:22:30 -08001877 event = mConnector.execute("cryptfs", "checkpw", new SensitiveArg(toHex(password)));
Jason parks9ed98bc2011-01-17 09:58:35 -06001878
Fredrik Roubertda6aedf2011-12-20 17:34:43 +01001879 final int code = Integer.parseInt(event.getMessage());
Jason parks9ed98bc2011-01-17 09:58:35 -06001880 if (code == 0) {
1881 // Decrypt was successful. Post a delayed message before restarting in order
1882 // to let the UI to clear itself
1883 mHandler.postDelayed(new Runnable() {
1884 public void run() {
Jeff Sharkey31c6e482011-11-18 17:09:01 -08001885 try {
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001886 mConnector.execute("cryptfs", "restart");
Jeff Sharkey31c6e482011-11-18 17:09:01 -08001887 } catch (NativeDaemonConnectorException e) {
1888 Slog.e(TAG, "problem executing in background", e);
1889 }
Jason parks9ed98bc2011-01-17 09:58:35 -06001890 }
Jason parksf7b3cd42011-01-27 09:28:25 -06001891 }, 1000); // 1 second
Jason parks9ed98bc2011-01-17 09:58:35 -06001892 }
1893
1894 return code;
Jason parks5af0b912010-11-29 09:05:25 -06001895 } catch (NativeDaemonConnectorException e) {
1896 // Decryption failed
1897 return e.getCode();
1898 }
Jason parks5af0b912010-11-29 09:05:25 -06001899 }
1900
Paul Lawrence46791e72014-04-03 09:10:26 -07001901 public int encryptStorage(int type, String password) {
1902 if (TextUtils.isEmpty(password) && type != StorageManager.CRYPT_TYPE_DEFAULT) {
Jason parksf7b3cd42011-01-27 09:28:25 -06001903 throw new IllegalArgumentException("password cannot be empty");
Jason parks56aa5322011-01-07 09:01:15 -06001904 }
1905
Jason parks8888c592011-01-20 22:46:41 -06001906 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
1907 "no permission to access the crypt keeper");
Jason parks56aa5322011-01-07 09:01:15 -06001908
1909 waitForReady();
1910
1911 if (DEBUG_EVENTS) {
Jason parks8888c592011-01-20 22:46:41 -06001912 Slog.i(TAG, "encrypting storage...");
Jason parks56aa5322011-01-07 09:01:15 -06001913 }
1914
1915 try {
Paul Lawrence46791e72014-04-03 09:10:26 -07001916 mConnector.execute("cryptfs", "enablecrypto", "inplace", CRYPTO_TYPES[type],
Paul Lawrence8e397362014-01-27 15:22:30 -08001917 new SensitiveArg(toHex(password)));
Jason parks56aa5322011-01-07 09:01:15 -06001918 } catch (NativeDaemonConnectorException e) {
1919 // Encryption failed
1920 return e.getCode();
1921 }
1922
1923 return 0;
1924 }
1925
Paul Lawrence8e397362014-01-27 15:22:30 -08001926 /** Set the password for encrypting the master key.
1927 * @param type One of the CRYPTO_TYPE_XXX consts defined in StorageManager.
1928 * @param password The password to set.
1929 */
1930 public int changeEncryptionPassword(int type, String password) {
Jason parksf7b3cd42011-01-27 09:28:25 -06001931 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
1932 "no permission to access the crypt keeper");
1933
1934 waitForReady();
1935
1936 if (DEBUG_EVENTS) {
1937 Slog.i(TAG, "changing encryption password...");
1938 }
1939
1940 try {
Svetoslava6711ff2014-10-17 11:38:06 -07001941 NativeDaemonEvent event = mConnector.execute("cryptfs", "changepw", CRYPTO_TYPES[type],
Svetoslav16e4a1a2014-09-29 18:16:20 -07001942 new SensitiveArg(toHex(password)));
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001943 return Integer.parseInt(event.getMessage());
Jason parksf7b3cd42011-01-27 09:28:25 -06001944 } catch (NativeDaemonConnectorException e) {
1945 // Encryption failed
1946 return e.getCode();
1947 }
1948 }
1949
Christopher Tate32418be2011-10-10 13:51:12 -07001950 /**
1951 * Validate a user-supplied password string with cryptfs
1952 */
1953 @Override
1954 public int verifyEncryptionPassword(String password) throws RemoteException {
1955 // Only the system process is permitted to validate passwords
1956 if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
1957 throw new SecurityException("no permission to access the crypt keeper");
1958 }
1959
1960 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
1961 "no permission to access the crypt keeper");
1962
1963 if (TextUtils.isEmpty(password)) {
1964 throw new IllegalArgumentException("password cannot be empty");
1965 }
1966
1967 waitForReady();
1968
1969 if (DEBUG_EVENTS) {
1970 Slog.i(TAG, "validating encryption password...");
1971 }
1972
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001973 final NativeDaemonEvent event;
Christopher Tate32418be2011-10-10 13:51:12 -07001974 try {
Paul Lawrence8e397362014-01-27 15:22:30 -08001975 event = mConnector.execute("cryptfs", "verifypw", new SensitiveArg(toHex(password)));
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08001976 Slog.i(TAG, "cryptfs verifypw => " + event.getMessage());
1977 return Integer.parseInt(event.getMessage());
Christopher Tate32418be2011-10-10 13:51:12 -07001978 } catch (NativeDaemonConnectorException e) {
1979 // Encryption failed
1980 return e.getCode();
1981 }
1982 }
1983
Paul Lawrence8e397362014-01-27 15:22:30 -08001984 /**
1985 * Get the type of encryption used to encrypt the master key.
1986 * @return The type, one of the CRYPT_TYPE_XXX consts from StorageManager.
1987 */
1988 @Override
Svetoslav16e4a1a2014-09-29 18:16:20 -07001989 public int getPasswordType() {
Paul Lawrence8e397362014-01-27 15:22:30 -08001990
1991 waitForReady();
1992
1993 final NativeDaemonEvent event;
1994 try {
1995 event = mConnector.execute("cryptfs", "getpwtype");
1996 for (int i = 0; i < CRYPTO_TYPES.length; ++i) {
1997 if (CRYPTO_TYPES[i].equals(event.getMessage()))
1998 return i;
1999 }
2000
2001 throw new IllegalStateException("unexpected return from cryptfs");
2002 } catch (NativeDaemonConnectorException e) {
2003 throw e.rethrowAsParcelableException();
2004 }
2005 }
2006
Paul Lawrencee51dcf92014-03-18 10:56:00 -07002007 /**
2008 * Set a field in the crypto header.
2009 * @param field field to set
2010 * @param contents contents to set in field
2011 */
2012 @Override
2013 public void setField(String field, String contents) throws RemoteException {
2014
2015 waitForReady();
2016
2017 final NativeDaemonEvent event;
2018 try {
2019 event = mConnector.execute("cryptfs", "setfield", field, contents);
2020 } catch (NativeDaemonConnectorException e) {
2021 throw e.rethrowAsParcelableException();
2022 }
2023 }
2024
2025 /**
2026 * Gets a field from the crypto header.
2027 * @param field field to get
2028 * @return contents of field
2029 */
2030 @Override
2031 public String getField(String field) throws RemoteException {
2032
2033 waitForReady();
2034
2035 final NativeDaemonEvent event;
2036 try {
2037 final String[] contents = NativeDaemonEvent.filterMessageList(
2038 mConnector.executeForList("cryptfs", "getfield", field),
2039 VoldResponseCode.CryptfsGetfieldResult);
2040 String result = new String();
2041 for (String content : contents) {
2042 result += content;
2043 }
2044 return result;
2045 } catch (NativeDaemonConnectorException e) {
2046 throw e.rethrowAsParcelableException();
2047 }
2048 }
2049
Jeff Sharkeyb049e212012-09-07 23:16:01 -07002050 @Override
Paul Lawrence945490c2014-03-27 16:37:28 +00002051 public String getPassword() throws RemoteException {
2052 if (!isReady()) {
2053 return new String();
2054 }
2055
2056 final NativeDaemonEvent event;
2057 try {
2058 event = mConnector.execute("cryptfs", "getpw");
Paul Lawrence24063b52015-01-06 13:11:23 -08002059 if ("-1".equals(event.getMessage())) {
2060 // -1 equals no password
2061 return null;
2062 }
Paul Lawrence945490c2014-03-27 16:37:28 +00002063 return fromHex(event.getMessage());
2064 } catch (NativeDaemonConnectorException e) {
2065 throw e.rethrowAsParcelableException();
Paul Lawrence24063b52015-01-06 13:11:23 -08002066 } catch (IllegalArgumentException e) {
2067 Slog.e(TAG, "Invalid response to getPassword");
2068 return null;
Paul Lawrence945490c2014-03-27 16:37:28 +00002069 }
2070 }
2071
2072 @Override
2073 public void clearPassword() throws RemoteException {
2074 if (!isReady()) {
2075 return;
2076 }
2077
2078 final NativeDaemonEvent event;
2079 try {
2080 event = mConnector.execute("cryptfs", "clearpw");
2081 } catch (NativeDaemonConnectorException e) {
2082 throw e.rethrowAsParcelableException();
2083 }
2084 }
2085
2086 @Override
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002087 public int mkdirs(String callingPkg, String appPath) {
2088 final int userId = UserHandle.getUserId(Binder.getCallingUid());
2089 final UserEnvironment userEnv = new UserEnvironment(userId);
2090
2091 // Validate that reported package name belongs to caller
2092 final AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(
2093 Context.APP_OPS_SERVICE);
2094 appOps.checkPackage(Binder.getCallingUid(), callingPkg);
2095
Jeff Sharkey48877892015-03-18 11:27:19 -07002096 File appFile = null;
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002097 try {
Jeff Sharkey48877892015-03-18 11:27:19 -07002098 appFile = new File(appPath).getCanonicalFile();
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002099 } catch (IOException e) {
2100 Slog.e(TAG, "Failed to resolve " + appPath + ": " + e);
2101 return -1;
2102 }
2103
2104 // Try translating the app path into a vold path, but require that it
2105 // belong to the calling package.
Jeff Sharkey48877892015-03-18 11:27:19 -07002106 if (FileUtils.contains(userEnv.buildExternalStorageAppDataDirs(callingPkg), appFile) ||
2107 FileUtils.contains(userEnv.buildExternalStorageAppObbDirs(callingPkg), appFile) ||
2108 FileUtils.contains(userEnv.buildExternalStorageAppMediaDirs(callingPkg), appFile)) {
2109 appPath = appFile.getAbsolutePath();
2110 if (!appPath.endsWith("/")) {
2111 appPath = appPath + "/";
2112 }
2113
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002114 try {
Jeff Sharkey48877892015-03-18 11:27:19 -07002115 mConnector.execute("volume", "mkdirs", appPath);
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002116 return 0;
2117 } catch (NativeDaemonConnectorException e) {
2118 return e.getCode();
2119 }
2120 }
2121
Jeff Sharkey48877892015-03-18 11:27:19 -07002122 throw new SecurityException("Invalid mkdirs path: " + appFile);
Jeff Sharkey2d8b4e82013-09-17 17:30:33 -07002123 }
2124
2125 @Override
Jeff Sharkey48877892015-03-18 11:27:19 -07002126 public StorageVolume[] getVolumeList(int userId) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002127 final ArrayList<StorageVolume> res = new ArrayList<>();
Jeff Sharkey48877892015-03-18 11:27:19 -07002128 boolean foundPrimary = false;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002129
Jeff Sharkey48877892015-03-18 11:27:19 -07002130 synchronized (mLock) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002131 for (int i = 0; i < mVolumes.size(); i++) {
2132 final VolumeInfo vol = mVolumes.valueAt(i);
Jeff Sharkey48877892015-03-18 11:27:19 -07002133 if (vol.isVisibleToUser(userId)) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002134 final StorageVolume userVol = vol.buildStorageVolume(mContext, userId);
Jeff Sharkey48877892015-03-18 11:27:19 -07002135 if (vol.isPrimary()) {
2136 res.add(0, userVol);
2137 foundPrimary = true;
2138 } else {
2139 res.add(userVol);
2140 }
Jeff Sharkeyb049e212012-09-07 23:16:01 -07002141 }
Mike Lockwood8fa5f802011-03-24 08:12:30 -07002142 }
Mike Lockwood8fa5f802011-03-24 08:12:30 -07002143 }
Jeff Sharkey48877892015-03-18 11:27:19 -07002144
2145 if (!foundPrimary) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002146 Log.w(TAG, "No primary storage defined yet; hacking together a stub");
Jeff Sharkey48877892015-03-18 11:27:19 -07002147
2148 final boolean primaryPhysical = SystemProperties.getBoolean(
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002149 StorageManager.PROP_PRIMARY_PHYSICAL, false);
Jeff Sharkey48877892015-03-18 11:27:19 -07002150
2151 final String id = "stub_primary";
2152 final File path = Environment.getLegacyExternalStorageDirectory();
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002153 final String description = mContext.getString(android.R.string.unknownName);
Jeff Sharkey48877892015-03-18 11:27:19 -07002154 final boolean primary = true;
2155 final boolean removable = primaryPhysical;
2156 final boolean emulated = !primaryPhysical;
2157 final long mtpReserveSize = 0L;
2158 final boolean allowMassStorage = false;
2159 final long maxFileSize = 0L;
2160 final UserHandle owner = new UserHandle(userId);
2161 final String uuid = null;
Jeff Sharkey48877892015-03-18 11:27:19 -07002162 final String state = Environment.MEDIA_REMOVED;
2163
2164 res.add(0, new StorageVolume(id, MtpStorage.getStorageIdForIndex(0), path,
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002165 description, primary, removable, emulated, mtpReserveSize,
2166 allowMassStorage, maxFileSize, owner, uuid, state));
Jeff Sharkey48877892015-03-18 11:27:19 -07002167 }
2168
2169 return res.toArray(new StorageVolume[res.size()]);
Mike Lockwood8fa5f802011-03-24 08:12:30 -07002170 }
2171
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002172 @Override
2173 public DiskInfo[] getDisks() {
2174 synchronized (mLock) {
2175 final DiskInfo[] res = new DiskInfo[mDisks.size()];
2176 for (int i = 0; i < mDisks.size(); i++) {
2177 res[i] = mDisks.valueAt(i);
2178 }
2179 return res;
2180 }
2181 }
2182
2183 @Override
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07002184 public VolumeInfo[] getVolumes(int flags) {
2185 if ((flags & StorageManager.FLAG_ALL_METADATA) != 0) {
2186 // TODO: implement support for returning all metadata
2187 throw new UnsupportedOperationException();
2188 }
2189
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07002190 synchronized (mLock) {
2191 final VolumeInfo[] res = new VolumeInfo[mVolumes.size()];
2192 for (int i = 0; i < mVolumes.size(); i++) {
2193 res[i] = mVolumes.valueAt(i);
2194 }
2195 return res;
2196 }
2197 }
2198
Kenny Rootaf9d6672010-10-08 09:21:39 -07002199 private void addObbStateLocked(ObbState obbState) throws RemoteException {
2200 final IBinder binder = obbState.getBinder();
2201 List<ObbState> obbStates = mObbMounts.get(binder);
Kenny Root5919ac62010-10-05 09:49:40 -07002202
Kenny Rootaf9d6672010-10-08 09:21:39 -07002203 if (obbStates == null) {
2204 obbStates = new ArrayList<ObbState>();
2205 mObbMounts.put(binder, obbStates);
2206 } else {
2207 for (final ObbState o : obbStates) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002208 if (o.rawPath.equals(obbState.rawPath)) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002209 throw new IllegalStateException("Attempt to add ObbState twice. "
2210 + "This indicates an error in the MountService logic.");
Kenny Root5919ac62010-10-05 09:49:40 -07002211 }
2212 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002213 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07002214
2215 obbStates.add(obbState);
2216 try {
2217 obbState.link();
2218 } catch (RemoteException e) {
2219 /*
2220 * The binder died before we could link it, so clean up our state
2221 * and return failure.
2222 */
2223 obbStates.remove(obbState);
2224 if (obbStates.isEmpty()) {
2225 mObbMounts.remove(binder);
2226 }
2227
2228 // Rethrow the error so mountObb can get it
2229 throw e;
2230 }
2231
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002232 mObbPathToStateMap.put(obbState.rawPath, obbState);
Kenny Roota02b8b02010-08-05 16:14:17 -07002233 }
2234
Kenny Rootaf9d6672010-10-08 09:21:39 -07002235 private void removeObbStateLocked(ObbState obbState) {
2236 final IBinder binder = obbState.getBinder();
2237 final List<ObbState> obbStates = mObbMounts.get(binder);
2238 if (obbStates != null) {
2239 if (obbStates.remove(obbState)) {
2240 obbState.unlink();
Kenny Root05105f72010-09-22 17:29:43 -07002241 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07002242 if (obbStates.isEmpty()) {
2243 mObbMounts.remove(binder);
2244 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002245 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002246
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002247 mObbPathToStateMap.remove(obbState.rawPath);
Kenny Root38cf8862010-09-26 14:18:51 -07002248 }
2249
Kenny Roota02b8b02010-08-05 16:14:17 -07002250 private class ObbActionHandler extends Handler {
2251 private boolean mBound = false;
Kenny Root480afe72010-10-07 10:17:50 -07002252 private final List<ObbAction> mActions = new LinkedList<ObbAction>();
Kenny Roota02b8b02010-08-05 16:14:17 -07002253
2254 ObbActionHandler(Looper l) {
2255 super(l);
2256 }
2257
2258 @Override
2259 public void handleMessage(Message msg) {
2260 switch (msg.what) {
2261 case OBB_RUN_ACTION: {
Kenny Root480afe72010-10-07 10:17:50 -07002262 final ObbAction action = (ObbAction) msg.obj;
Kenny Roota02b8b02010-08-05 16:14:17 -07002263
2264 if (DEBUG_OBB)
2265 Slog.i(TAG, "OBB_RUN_ACTION: " + action.toString());
2266
2267 // If a bind was already initiated we don't really
2268 // need to do anything. The pending install
2269 // will be processed later on.
2270 if (!mBound) {
2271 // If this is the only one pending we might
2272 // have to bind to the service again.
2273 if (!connectToService()) {
2274 Slog.e(TAG, "Failed to bind to media container service");
2275 action.handleError();
2276 return;
Kenny Roota02b8b02010-08-05 16:14:17 -07002277 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002278 }
Kenny Root735de3b2010-09-30 14:11:39 -07002279
Kenny Root735de3b2010-09-30 14:11:39 -07002280 mActions.add(action);
Kenny Roota02b8b02010-08-05 16:14:17 -07002281 break;
2282 }
2283 case OBB_MCS_BOUND: {
2284 if (DEBUG_OBB)
2285 Slog.i(TAG, "OBB_MCS_BOUND");
2286 if (msg.obj != null) {
2287 mContainerService = (IMediaContainerService) msg.obj;
2288 }
2289 if (mContainerService == null) {
2290 // Something seriously wrong. Bail out
2291 Slog.e(TAG, "Cannot bind to media container service");
2292 for (ObbAction action : mActions) {
2293 // Indicate service bind error
2294 action.handleError();
2295 }
2296 mActions.clear();
2297 } else if (mActions.size() > 0) {
Kenny Root480afe72010-10-07 10:17:50 -07002298 final ObbAction action = mActions.get(0);
Kenny Roota02b8b02010-08-05 16:14:17 -07002299 if (action != null) {
2300 action.execute(this);
2301 }
2302 } else {
2303 // Should never happen ideally.
2304 Slog.w(TAG, "Empty queue");
2305 }
2306 break;
2307 }
2308 case OBB_MCS_RECONNECT: {
2309 if (DEBUG_OBB)
2310 Slog.i(TAG, "OBB_MCS_RECONNECT");
2311 if (mActions.size() > 0) {
2312 if (mBound) {
2313 disconnectService();
2314 }
2315 if (!connectToService()) {
2316 Slog.e(TAG, "Failed to bind to media container service");
2317 for (ObbAction action : mActions) {
2318 // Indicate service bind error
2319 action.handleError();
2320 }
2321 mActions.clear();
2322 }
2323 }
2324 break;
2325 }
2326 case OBB_MCS_UNBIND: {
2327 if (DEBUG_OBB)
2328 Slog.i(TAG, "OBB_MCS_UNBIND");
2329
2330 // Delete pending install
2331 if (mActions.size() > 0) {
2332 mActions.remove(0);
2333 }
2334 if (mActions.size() == 0) {
2335 if (mBound) {
2336 disconnectService();
2337 }
2338 } else {
2339 // There are more pending requests in queue.
2340 // Just post MCS_BOUND message to trigger processing
2341 // of next pending install.
2342 mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND);
2343 }
2344 break;
2345 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07002346 case OBB_FLUSH_MOUNT_STATE: {
2347 final String path = (String) msg.obj;
2348
2349 if (DEBUG_OBB)
2350 Slog.i(TAG, "Flushing all OBB state for path " + path);
2351
2352 synchronized (mObbMounts) {
2353 final List<ObbState> obbStatesToRemove = new LinkedList<ObbState>();
2354
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002355 final Iterator<ObbState> i = mObbPathToStateMap.values().iterator();
Kenny Rootaf9d6672010-10-08 09:21:39 -07002356 while (i.hasNext()) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002357 final ObbState state = i.next();
Kenny Rootaf9d6672010-10-08 09:21:39 -07002358
2359 /*
2360 * If this entry's source file is in the volume path
2361 * that got unmounted, remove it because it's no
2362 * longer valid.
2363 */
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002364 if (state.canonicalPath.startsWith(path)) {
2365 obbStatesToRemove.add(state);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002366 }
2367 }
2368
2369 for (final ObbState obbState : obbStatesToRemove) {
2370 if (DEBUG_OBB)
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002371 Slog.i(TAG, "Removing state for " + obbState.rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002372
2373 removeObbStateLocked(obbState);
2374
2375 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002376 obbState.token.onObbResult(obbState.rawPath, obbState.nonce,
Kenny Rootaf9d6672010-10-08 09:21:39 -07002377 OnObbStateChangeListener.UNMOUNTED);
2378 } catch (RemoteException e) {
2379 Slog.i(TAG, "Couldn't send unmount notification for OBB: "
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002380 + obbState.rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002381 }
2382 }
2383 }
2384 break;
2385 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002386 }
2387 }
2388
2389 private boolean connectToService() {
2390 if (DEBUG_OBB)
2391 Slog.i(TAG, "Trying to bind to DefaultContainerService");
2392
2393 Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
2394 if (mContext.bindService(service, mDefContainerConn, Context.BIND_AUTO_CREATE)) {
2395 mBound = true;
2396 return true;
2397 }
2398 return false;
2399 }
2400
2401 private void disconnectService() {
2402 mContainerService = null;
2403 mBound = false;
2404 mContext.unbindService(mDefContainerConn);
2405 }
2406 }
2407
2408 abstract class ObbAction {
2409 private static final int MAX_RETRIES = 3;
2410 private int mRetries;
2411
2412 ObbState mObbState;
2413
2414 ObbAction(ObbState obbState) {
2415 mObbState = obbState;
2416 }
2417
2418 public void execute(ObbActionHandler handler) {
2419 try {
2420 if (DEBUG_OBB)
Ben Komalo444eca22011-09-01 15:17:44 -07002421 Slog.i(TAG, "Starting to execute action: " + toString());
Kenny Roota02b8b02010-08-05 16:14:17 -07002422 mRetries++;
2423 if (mRetries > MAX_RETRIES) {
2424 Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
Kenny Root480afe72010-10-07 10:17:50 -07002425 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
Kenny Roota02b8b02010-08-05 16:14:17 -07002426 handleError();
2427 return;
2428 } else {
2429 handleExecute();
2430 if (DEBUG_OBB)
2431 Slog.i(TAG, "Posting install MCS_UNBIND");
2432 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
2433 }
2434 } catch (RemoteException e) {
2435 if (DEBUG_OBB)
2436 Slog.i(TAG, "Posting install MCS_RECONNECT");
2437 mObbActionHandler.sendEmptyMessage(OBB_MCS_RECONNECT);
2438 } catch (Exception e) {
2439 if (DEBUG_OBB)
2440 Slog.d(TAG, "Error handling OBB action", e);
2441 handleError();
Kenny Root17eb6fb2010-10-06 15:02:52 -07002442 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
Kenny Roota02b8b02010-08-05 16:14:17 -07002443 }
2444 }
2445
Kenny Root05105f72010-09-22 17:29:43 -07002446 abstract void handleExecute() throws RemoteException, IOException;
Kenny Roota02b8b02010-08-05 16:14:17 -07002447 abstract void handleError();
Kenny Root38cf8862010-09-26 14:18:51 -07002448
2449 protected ObbInfo getObbInfo() throws IOException {
2450 ObbInfo obbInfo;
2451 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002452 obbInfo = mContainerService.getObbInfo(mObbState.ownerPath);
Kenny Root38cf8862010-09-26 14:18:51 -07002453 } catch (RemoteException e) {
2454 Slog.d(TAG, "Couldn't call DefaultContainerService to fetch OBB info for "
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002455 + mObbState.ownerPath);
Kenny Root38cf8862010-09-26 14:18:51 -07002456 obbInfo = null;
2457 }
2458 if (obbInfo == null) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002459 throw new IOException("Couldn't read OBB file: " + mObbState.ownerPath);
Kenny Root38cf8862010-09-26 14:18:51 -07002460 }
2461 return obbInfo;
2462 }
2463
Kenny Rootaf9d6672010-10-08 09:21:39 -07002464 protected void sendNewStatusOrIgnore(int status) {
2465 if (mObbState == null || mObbState.token == null) {
2466 return;
2467 }
2468
Kenny Root38cf8862010-09-26 14:18:51 -07002469 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002470 mObbState.token.onObbResult(mObbState.rawPath, mObbState.nonce, status);
Kenny Root38cf8862010-09-26 14:18:51 -07002471 } catch (RemoteException e) {
2472 Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
2473 }
2474 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002475 }
2476
2477 class MountObbAction extends ObbAction {
Ben Komalo444eca22011-09-01 15:17:44 -07002478 private final String mKey;
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002479 private final int mCallingUid;
Kenny Roota02b8b02010-08-05 16:14:17 -07002480
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002481 MountObbAction(ObbState obbState, String key, int callingUid) {
Kenny Roota02b8b02010-08-05 16:14:17 -07002482 super(obbState);
2483 mKey = key;
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002484 mCallingUid = callingUid;
Kenny Roota02b8b02010-08-05 16:14:17 -07002485 }
2486
Jason parks5af0b912010-11-29 09:05:25 -06002487 @Override
Kenny Root735de3b2010-09-30 14:11:39 -07002488 public void handleExecute() throws IOException, RemoteException {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002489 waitForReady();
2490 warnOnNotMounted();
2491
Kenny Root38cf8862010-09-26 14:18:51 -07002492 final ObbInfo obbInfo = getObbInfo();
2493
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002494 if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mCallingUid)) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002495 Slog.w(TAG, "Denied attempt to mount OBB " + obbInfo.filename
2496 + " which is owned by " + obbInfo.packageName);
2497 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
2498 return;
Kenny Roota02b8b02010-08-05 16:14:17 -07002499 }
2500
Kenny Rootaf9d6672010-10-08 09:21:39 -07002501 final boolean isMounted;
2502 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002503 isMounted = mObbPathToStateMap.containsKey(mObbState.rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002504 }
2505 if (isMounted) {
2506 Slog.w(TAG, "Attempt to mount OBB which is already mounted: " + obbInfo.filename);
2507 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_ALREADY_MOUNTED);
2508 return;
2509 }
2510
Kenny Rootaf9d6672010-10-08 09:21:39 -07002511 final String hashedKey;
2512 if (mKey == null) {
2513 hashedKey = "none";
2514 } else {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002515 try {
Kenny Root3b1abba2010-10-13 15:00:07 -07002516 SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
2517
2518 KeySpec ks = new PBEKeySpec(mKey.toCharArray(), obbInfo.salt,
2519 PBKDF2_HASH_ROUNDS, CRYPTO_ALGORITHM_KEY_SIZE);
2520 SecretKey key = factory.generateSecret(ks);
2521 BigInteger bi = new BigInteger(key.getEncoded());
2522 hashedKey = bi.toString(16);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002523 } catch (NoSuchAlgorithmException e) {
Kenny Root3b1abba2010-10-13 15:00:07 -07002524 Slog.e(TAG, "Could not load PBKDF2 algorithm", e);
2525 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
2526 return;
2527 } catch (InvalidKeySpecException e) {
2528 Slog.e(TAG, "Invalid key spec when loading PBKDF2 algorithm", e);
2529 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
Kenny Root38cf8862010-09-26 14:18:51 -07002530 return;
2531 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07002532 }
Kenny Root38cf8862010-09-26 14:18:51 -07002533
Kenny Rootaf9d6672010-10-08 09:21:39 -07002534 int rc = StorageResultCode.OperationSucceeded;
Kenny Rootaf9d6672010-10-08 09:21:39 -07002535 try {
Jeff Sharkey56cd6462013-06-07 15:09:15 -07002536 mConnector.execute("obb", "mount", mObbState.voldPath, new SensitiveArg(hashedKey),
2537 mObbState.ownerGid);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002538 } catch (NativeDaemonConnectorException e) {
2539 int code = e.getCode();
2540 if (code != VoldResponseCode.OpFailedStorageBusy) {
2541 rc = StorageResultCode.OperationFailedInternalError;
Kenny Roota02b8b02010-08-05 16:14:17 -07002542 }
2543 }
2544
Kenny Rootaf9d6672010-10-08 09:21:39 -07002545 if (rc == StorageResultCode.OperationSucceeded) {
2546 if (DEBUG_OBB)
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002547 Slog.d(TAG, "Successfully mounted OBB " + mObbState.voldPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002548
2549 synchronized (mObbMounts) {
2550 addObbStateLocked(mObbState);
2551 }
2552
2553 sendNewStatusOrIgnore(OnObbStateChangeListener.MOUNTED);
Kenny Root02c87302010-07-01 08:10:18 -07002554 } else {
Kenny Root05105f72010-09-22 17:29:43 -07002555 Slog.e(TAG, "Couldn't mount OBB file: " + rc);
Kenny Roota02b8b02010-08-05 16:14:17 -07002556
Kenny Rootaf9d6672010-10-08 09:21:39 -07002557 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT);
Kenny Root02c87302010-07-01 08:10:18 -07002558 }
2559 }
2560
Jason parks5af0b912010-11-29 09:05:25 -06002561 @Override
Kenny Roota02b8b02010-08-05 16:14:17 -07002562 public void handleError() {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002563 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
Kenny Root02c87302010-07-01 08:10:18 -07002564 }
Kenny Roota02b8b02010-08-05 16:14:17 -07002565
2566 @Override
2567 public String toString() {
2568 StringBuilder sb = new StringBuilder();
2569 sb.append("MountObbAction{");
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002570 sb.append(mObbState);
Kenny Roota02b8b02010-08-05 16:14:17 -07002571 sb.append('}');
2572 return sb.toString();
2573 }
2574 }
2575
2576 class UnmountObbAction extends ObbAction {
Ben Komalo444eca22011-09-01 15:17:44 -07002577 private final boolean mForceUnmount;
Kenny Roota02b8b02010-08-05 16:14:17 -07002578
2579 UnmountObbAction(ObbState obbState, boolean force) {
2580 super(obbState);
2581 mForceUnmount = force;
2582 }
2583
Jason parks5af0b912010-11-29 09:05:25 -06002584 @Override
Kenny Root38cf8862010-09-26 14:18:51 -07002585 public void handleExecute() throws IOException {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002586 waitForReady();
2587 warnOnNotMounted();
2588
Kenny Root38cf8862010-09-26 14:18:51 -07002589 final ObbInfo obbInfo = getObbInfo();
Kenny Roota02b8b02010-08-05 16:14:17 -07002590
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002591 final ObbState existingState;
Kenny Root38cf8862010-09-26 14:18:51 -07002592 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002593 existingState = mObbPathToStateMap.get(mObbState.rawPath);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002594 }
Kenny Root38cf8862010-09-26 14:18:51 -07002595
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002596 if (existingState == null) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002597 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_NOT_MOUNTED);
2598 return;
2599 }
2600
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002601 if (existingState.ownerGid != mObbState.ownerGid) {
2602 Slog.w(TAG, "Permission denied attempting to unmount OBB " + existingState.rawPath
2603 + " (owned by GID " + existingState.ownerGid + ")");
Kenny Rootaf9d6672010-10-08 09:21:39 -07002604 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
2605 return;
2606 }
2607
Kenny Rootaf9d6672010-10-08 09:21:39 -07002608 int rc = StorageResultCode.OperationSucceeded;
Kenny Rootaf9d6672010-10-08 09:21:39 -07002609 try {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002610 final Command cmd = new Command("obb", "unmount", mObbState.voldPath);
Jeff Sharkeydd519fa2011-12-02 14:11:21 -08002611 if (mForceUnmount) {
2612 cmd.appendArg("force");
2613 }
2614 mConnector.execute(cmd);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002615 } catch (NativeDaemonConnectorException e) {
2616 int code = e.getCode();
2617 if (code == VoldResponseCode.OpFailedStorageBusy) {
2618 rc = StorageResultCode.OperationFailedStorageBusy;
2619 } else if (code == VoldResponseCode.OpFailedStorageNotFound) {
2620 // If it's not mounted then we've already won.
2621 rc = StorageResultCode.OperationSucceeded;
2622 } else {
2623 rc = StorageResultCode.OperationFailedInternalError;
Kenny Roota02b8b02010-08-05 16:14:17 -07002624 }
2625 }
2626
Kenny Rootaf9d6672010-10-08 09:21:39 -07002627 if (rc == StorageResultCode.OperationSucceeded) {
2628 synchronized (mObbMounts) {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002629 removeObbStateLocked(existingState);
Kenny Root38cf8862010-09-26 14:18:51 -07002630 }
2631
Kenny Rootaf9d6672010-10-08 09:21:39 -07002632 sendNewStatusOrIgnore(OnObbStateChangeListener.UNMOUNTED);
Kenny Roota02b8b02010-08-05 16:14:17 -07002633 } else {
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002634 Slog.w(TAG, "Could not unmount OBB: " + existingState);
Kenny Rootaf9d6672010-10-08 09:21:39 -07002635 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_UNMOUNT);
Kenny Roota02b8b02010-08-05 16:14:17 -07002636 }
2637 }
2638
Jason parks5af0b912010-11-29 09:05:25 -06002639 @Override
Kenny Roota02b8b02010-08-05 16:14:17 -07002640 public void handleError() {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002641 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
Kenny Roota02b8b02010-08-05 16:14:17 -07002642 }
2643
2644 @Override
2645 public String toString() {
2646 StringBuilder sb = new StringBuilder();
2647 sb.append("UnmountObbAction{");
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002648 sb.append(mObbState);
Kenny Roota02b8b02010-08-05 16:14:17 -07002649 sb.append(",force=");
2650 sb.append(mForceUnmount);
Kenny Roota02b8b02010-08-05 16:14:17 -07002651 sb.append('}');
2652 return sb.toString();
2653 }
Kenny Root02c87302010-07-01 08:10:18 -07002654 }
Kenny Root38cf8862010-09-26 14:18:51 -07002655
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -08002656 @VisibleForTesting
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002657 public static String buildObbPath(final String canonicalPath, int userId, boolean forVold) {
2658 // TODO: allow caller to provide Environment for full testing
Jeff Sharkey1abdb712013-08-11 16:28:14 -07002659 // TODO: extend to support OBB mounts on secondary external storage
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002660
2661 // Only adjust paths when storage is emulated
2662 if (!Environment.isExternalStorageEmulated()) {
2663 return canonicalPath;
2664 }
2665
2666 String path = canonicalPath.toString();
2667
2668 // First trim off any external storage prefix
2669 final UserEnvironment userEnv = new UserEnvironment(userId);
2670
2671 // /storage/emulated/0
Jeff Sharkey1abdb712013-08-11 16:28:14 -07002672 final String externalPath = userEnv.getExternalStorageDirectory().getAbsolutePath();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002673 // /storage/emulated_legacy
2674 final String legacyExternalPath = Environment.getLegacyExternalStorageDirectory()
Jeff Sharkey1abdb712013-08-11 16:28:14 -07002675 .getAbsolutePath();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002676
2677 if (path.startsWith(externalPath)) {
2678 path = path.substring(externalPath.length() + 1);
2679 } else if (path.startsWith(legacyExternalPath)) {
2680 path = path.substring(legacyExternalPath.length() + 1);
2681 } else {
2682 return canonicalPath;
2683 }
2684
2685 // Handle special OBB paths on emulated storage
2686 final String obbPath = "Android/obb";
2687 if (path.startsWith(obbPath)) {
2688 path = path.substring(obbPath.length() + 1);
2689
Jeff Sharkey48877892015-03-18 11:27:19 -07002690 final UserEnvironment ownerEnv = new UserEnvironment(UserHandle.USER_OWNER);
2691 return new File(ownerEnv.buildExternalStorageAndroidObbDirs()[0], path)
2692 .getAbsolutePath();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002693 }
2694
2695 // Handle normal external storage paths
Jeff Sharkey48877892015-03-18 11:27:19 -07002696 return new File(userEnv.getExternalStorageDirectory(), path).getAbsolutePath();
Jeff Sharkey4fbbda42012-09-24 18:34:07 -07002697 }
2698
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07002699 private static class Callbacks extends Handler {
2700 private static final int MSG_STORAGE_STATE_CHANGED = 1;
2701 private static final int MSG_VOLUME_STATE_CHANGED = 2;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07002702 private static final int MSG_VOLUME_METADATA_CHANGED = 3;
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07002703 private static final int MSG_DISK_UNSUPPORTED = 4;
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07002704
2705 private final RemoteCallbackList<IMountServiceListener>
2706 mCallbacks = new RemoteCallbackList<>();
2707
2708 public Callbacks(Looper looper) {
2709 super(looper);
2710 }
2711
2712 public void register(IMountServiceListener callback) {
2713 mCallbacks.register(callback);
2714 }
2715
2716 public void unregister(IMountServiceListener callback) {
2717 mCallbacks.unregister(callback);
2718 }
2719
2720 @Override
2721 public void handleMessage(Message msg) {
2722 final SomeArgs args = (SomeArgs) msg.obj;
2723 final int n = mCallbacks.beginBroadcast();
2724 for (int i = 0; i < n; i++) {
2725 final IMountServiceListener callback = mCallbacks.getBroadcastItem(i);
2726 try {
2727 invokeCallback(callback, msg.what, args);
2728 } catch (RemoteException ignored) {
2729 }
2730 }
2731 mCallbacks.finishBroadcast();
2732 args.recycle();
2733 }
2734
2735 private void invokeCallback(IMountServiceListener callback, int what, SomeArgs args)
2736 throws RemoteException {
2737 switch (what) {
2738 case MSG_STORAGE_STATE_CHANGED: {
2739 callback.onStorageStateChanged((String) args.arg1, (String) args.arg2,
2740 (String) args.arg3);
2741 break;
2742 }
2743 case MSG_VOLUME_STATE_CHANGED: {
2744 callback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3);
2745 break;
2746 }
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07002747 case MSG_VOLUME_METADATA_CHANGED: {
2748 callback.onVolumeMetadataChanged((VolumeInfo) args.arg1);
2749 break;
2750 }
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07002751 case MSG_DISK_UNSUPPORTED: {
2752 callback.onDiskUnsupported((DiskInfo) args.arg1);
2753 break;
2754 }
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07002755 }
2756 }
2757
2758 private void notifyStorageStateChanged(String path, String oldState, String newState) {
2759 final SomeArgs args = SomeArgs.obtain();
2760 args.arg1 = path;
2761 args.arg2 = oldState;
2762 args.arg3 = newState;
2763 obtainMessage(MSG_STORAGE_STATE_CHANGED, args).sendToTarget();
2764 }
2765
2766 private void notifyVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
2767 final SomeArgs args = SomeArgs.obtain();
2768 args.arg1 = vol;
2769 args.argi2 = oldState;
2770 args.argi3 = newState;
2771 obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget();
2772 }
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -07002773
2774 private void notifyVolumeMetadataChanged(VolumeInfo vol) {
2775 final SomeArgs args = SomeArgs.obtain();
2776 args.arg1 = vol;
2777 obtainMessage(MSG_VOLUME_METADATA_CHANGED, args).sendToTarget();
2778 }
Jeff Sharkey7e92ef32015-04-17 17:35:07 -07002779
2780 private void notifyDiskUnsupported(DiskInfo disk) {
2781 final SomeArgs args = SomeArgs.obtain();
2782 args.arg1 = disk;
2783 obtainMessage(MSG_DISK_UNSUPPORTED, args).sendToTarget();
2784 }
Jeff Sharkey7151a9a2015-04-04 15:22:37 -07002785 }
2786
Kenny Root38cf8862010-09-26 14:18:51 -07002787 @Override
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002788 protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
2789 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
2790
Jeff Sharkey27de30d2015-04-18 16:20:27 -07002791 for (String arg : args) {
2792 if ("--clear-metadata".equals(arg)) {
2793 synchronized (mLock) {
2794 mMetadata.clear();
2795 writeMetadataLocked();
2796 }
2797 }
2798 }
2799
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002800 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ", 160);
Jeff Sharkey27de30d2015-04-18 16:20:27 -07002801 synchronized (mLock) {
2802 pw.println("Disks:");
2803 pw.increaseIndent();
2804 for (int i = 0; i < mDisks.size(); i++) {
2805 final DiskInfo disk = mDisks.valueAt(i);
2806 disk.dump(pw);
2807 }
2808 pw.decreaseIndent();
2809
2810 pw.println();
2811 pw.println("Volumes:");
2812 pw.increaseIndent();
2813 for (int i = 0; i < mVolumes.size(); i++) {
2814 final VolumeInfo vol = mVolumes.valueAt(i);
2815 if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.id)) continue;
2816 vol.dump(pw);
2817 }
2818 pw.decreaseIndent();
2819
2820 pw.println();
2821 pw.println("Metadata:");
2822 pw.increaseIndent();
2823 for (int i = 0; i < mMetadata.size(); i++) {
2824 final VolumeMetadata meta = mMetadata.valueAt(i);
2825 meta.dump(pw);
2826 }
2827 pw.decreaseIndent();
2828 }
Kenny Root38cf8862010-09-26 14:18:51 -07002829
Kenny Root38cf8862010-09-26 14:18:51 -07002830 synchronized (mObbMounts) {
Jeff Sharkey27de30d2015-04-18 16:20:27 -07002831 pw.println();
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002832 pw.println("mObbMounts:");
2833 pw.increaseIndent();
2834 final Iterator<Entry<IBinder, List<ObbState>>> binders = mObbMounts.entrySet()
2835 .iterator();
Kenny Rootaf9d6672010-10-08 09:21:39 -07002836 while (binders.hasNext()) {
2837 Entry<IBinder, List<ObbState>> e = binders.next();
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002838 pw.println(e.getKey() + ":");
2839 pw.increaseIndent();
Kenny Rootaf9d6672010-10-08 09:21:39 -07002840 final List<ObbState> obbStates = e.getValue();
Kenny Root38cf8862010-09-26 14:18:51 -07002841 for (final ObbState obbState : obbStates) {
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002842 pw.println(obbState);
Kenny Root38cf8862010-09-26 14:18:51 -07002843 }
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002844 pw.decreaseIndent();
Kenny Root38cf8862010-09-26 14:18:51 -07002845 }
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002846 pw.decreaseIndent();
Kenny Rootaf9d6672010-10-08 09:21:39 -07002847
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002848 pw.println();
2849 pw.println("mObbPathToStateMap:");
2850 pw.increaseIndent();
Kenny Rootaf9d6672010-10-08 09:21:39 -07002851 final Iterator<Entry<String, ObbState>> maps = mObbPathToStateMap.entrySet().iterator();
2852 while (maps.hasNext()) {
2853 final Entry<String, ObbState> e = maps.next();
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002854 pw.print(e.getKey());
2855 pw.print(" -> ");
2856 pw.println(e.getValue());
Kenny Rootaf9d6672010-10-08 09:21:39 -07002857 }
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002858 pw.decreaseIndent();
Kenny Root38cf8862010-09-26 14:18:51 -07002859 }
Kenny Root4161f9b2011-07-13 09:48:33 -07002860
Robert Greenwalt470fd722012-01-18 12:51:15 -08002861 pw.println();
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002862 pw.println("mConnection:");
2863 pw.increaseIndent();
Robert Greenwalt470fd722012-01-18 12:51:15 -08002864 mConnector.dump(fd, pw, args);
Jeff Sharkey5aca2b82013-10-16 16:21:54 -07002865 pw.decreaseIndent();
Christopher Tate7265abe2014-11-21 13:54:45 -08002866
2867 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
2868
2869 pw.println();
2870 pw.print("Last maintenance: ");
2871 pw.println(sdf.format(new Date(mLastMaintenance)));
Kenny Root38cf8862010-09-26 14:18:51 -07002872 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002873
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -07002874 /** {@inheritDoc} */
Jeff Sharkey48877892015-03-18 11:27:19 -07002875 @Override
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -07002876 public void monitor() {
2877 if (mConnector != null) {
2878 mConnector.monitor();
2879 }
2880 }
2881}