• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.server;
18 
19 import static com.android.internal.util.XmlUtils.readBooleanAttribute;
20 import static com.android.internal.util.XmlUtils.readIntAttribute;
21 import static com.android.internal.util.XmlUtils.readLongAttribute;
22 import static com.android.internal.util.XmlUtils.readStringAttribute;
23 import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
24 import static com.android.internal.util.XmlUtils.writeIntAttribute;
25 import static com.android.internal.util.XmlUtils.writeLongAttribute;
26 import static com.android.internal.util.XmlUtils.writeStringAttribute;
27 
28 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
29 import static org.xmlpull.v1.XmlPullParser.START_TAG;
30 
31 import android.Manifest;
32 import android.annotation.Nullable;
33 import android.app.ActivityManager;
34 import android.app.ActivityManagerNative;
35 import android.app.AppOpsManager;
36 import android.app.IActivityManager;
37 import android.content.BroadcastReceiver;
38 import android.content.ComponentName;
39 import android.content.Context;
40 import android.content.Intent;
41 import android.content.IntentFilter;
42 import android.content.ServiceConnection;
43 import android.content.pm.IPackageMoveObserver;
44 import android.content.pm.PackageManager;
45 import android.content.pm.ProviderInfo;
46 import android.content.pm.UserInfo;
47 import android.content.res.Configuration;
48 import android.content.res.ObbInfo;
49 import android.net.Uri;
50 import android.os.Binder;
51 import android.os.DropBoxManager;
52 import android.os.Environment;
53 import android.os.Environment.UserEnvironment;
54 import android.os.FileUtils;
55 import android.os.Handler;
56 import android.os.HandlerThread;
57 import android.os.IBinder;
58 import android.os.Looper;
59 import android.os.Message;
60 import android.os.ParcelFileDescriptor;
61 import android.os.PowerManager;
62 import android.os.Process;
63 import android.os.RemoteCallbackList;
64 import android.os.RemoteException;
65 import android.os.ServiceManager;
66 import android.os.SystemClock;
67 import android.os.SystemProperties;
68 import android.os.UserHandle;
69 import android.os.UserManager;
70 import android.os.storage.DiskInfo;
71 import android.os.storage.IMountService;
72 import android.os.storage.IMountServiceListener;
73 import android.os.storage.IMountShutdownObserver;
74 import android.os.storage.IObbActionListener;
75 import android.os.storage.MountServiceInternal;
76 import android.os.storage.OnObbStateChangeListener;
77 import android.os.storage.StorageManager;
78 import android.os.storage.StorageResultCode;
79 import android.os.storage.StorageVolume;
80 import android.os.storage.VolumeInfo;
81 import android.os.storage.VolumeRecord;
82 import android.provider.MediaStore;
83 import android.provider.Settings;
84 import android.text.TextUtils;
85 import android.text.format.DateUtils;
86 import android.util.ArrayMap;
87 import android.util.AtomicFile;
88 import android.util.Log;
89 import android.util.Slog;
90 import android.util.TimeUtils;
91 import android.util.Xml;
92 
93 import com.android.internal.annotations.GuardedBy;
94 import com.android.internal.app.IMediaContainerService;
95 import com.android.internal.os.SomeArgs;
96 import com.android.internal.os.Zygote;
97 import com.android.internal.util.ArrayUtils;
98 import com.android.internal.util.FastXmlSerializer;
99 import com.android.internal.util.HexDump;
100 import com.android.internal.util.IndentingPrintWriter;
101 import com.android.internal.util.Preconditions;
102 import com.android.internal.widget.LockPatternUtils;
103 import com.android.server.NativeDaemonConnector.Command;
104 import com.android.server.NativeDaemonConnector.SensitiveArg;
105 import com.android.server.pm.PackageManagerService;
106 
107 import libcore.io.IoUtils;
108 import libcore.util.EmptyArray;
109 
110 import org.xmlpull.v1.XmlPullParser;
111 import org.xmlpull.v1.XmlPullParserException;
112 import org.xmlpull.v1.XmlSerializer;
113 
114 import java.io.File;
115 import java.io.FileDescriptor;
116 import java.io.FileInputStream;
117 import java.io.FileNotFoundException;
118 import java.io.FileOutputStream;
119 import java.io.IOException;
120 import java.io.PrintWriter;
121 import java.math.BigInteger;
122 import java.nio.charset.StandardCharsets;
123 import java.security.NoSuchAlgorithmException;
124 import java.security.spec.InvalidKeySpecException;
125 import java.security.spec.KeySpec;
126 import java.util.ArrayList;
127 import java.util.Arrays;
128 import java.util.HashMap;
129 import java.util.HashSet;
130 import java.util.Iterator;
131 import java.util.LinkedList;
132 import java.util.List;
133 import java.util.Locale;
134 import java.util.Map;
135 import java.util.Map.Entry;
136 import java.util.Objects;
137 import java.util.concurrent.CopyOnWriteArrayList;
138 import java.util.concurrent.CountDownLatch;
139 import java.util.concurrent.TimeUnit;
140 import java.util.concurrent.TimeoutException;
141 
142 import javax.crypto.SecretKey;
143 import javax.crypto.SecretKeyFactory;
144 import javax.crypto.spec.PBEKeySpec;
145 
146 /**
147  * Service responsible for various storage media. Connects to {@code vold} to
148  * watch for and manage dynamically added storage, such as SD cards and USB mass
149  * storage. Also decides how storage should be presented to users on the device.
150  */
151 class MountService extends IMountService.Stub
152         implements INativeDaemonConnectorCallbacks, Watchdog.Monitor {
153 
154     // Static direct instance pointer for the tightly-coupled idle service to use
155     static MountService sSelf = null;
156 
157     public static class Lifecycle extends SystemService {
158         private MountService mMountService;
159 
Lifecycle(Context context)160         public Lifecycle(Context context) {
161             super(context);
162         }
163 
164         @Override
onStart()165         public void onStart() {
166             mMountService = new MountService(getContext());
167             publishBinderService("mount", mMountService);
168             mMountService.start();
169         }
170 
171         @Override
onBootPhase(int phase)172         public void onBootPhase(int phase) {
173             if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
174                 mMountService.systemReady();
175             } else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
176                 mMountService.bootCompleted();
177             }
178         }
179 
180         @Override
onSwitchUser(int userHandle)181         public void onSwitchUser(int userHandle) {
182             mMountService.mCurrentUserId = userHandle;
183         }
184 
185         @Override
onUnlockUser(int userHandle)186         public void onUnlockUser(int userHandle) {
187             mMountService.onUnlockUser(userHandle);
188         }
189 
190         @Override
onCleanupUser(int userHandle)191         public void onCleanupUser(int userHandle) {
192             mMountService.onCleanupUser(userHandle);
193         }
194     }
195 
196     private static final boolean DEBUG_EVENTS = false;
197     private static final boolean DEBUG_OBB = false;
198 
199     // Disable this since it messes up long-running cryptfs operations.
200     private static final boolean WATCHDOG_ENABLE = false;
201 
202     private static final String TAG = "MountService";
203 
204     private static final String TAG_STORAGE_BENCHMARK = "storage_benchmark";
205     private static final String TAG_STORAGE_TRIM = "storage_trim";
206 
207     private static final String VOLD_TAG = "VoldConnector";
208     private static final String CRYPTD_TAG = "CryptdConnector";
209 
210     /** Maximum number of ASEC containers allowed to be mounted. */
211     private static final int MAX_CONTAINERS = 250;
212 
213     /** Magic value sent by MoveTask.cpp */
214     private static final int MOVE_STATUS_COPY_FINISHED = 82;
215 
216     /*
217      * Internal vold response code constants
218      */
219     class VoldResponseCode {
220         /*
221          * 100 series - Requestion action was initiated; expect another reply
222          *              before proceeding with a new command.
223          */
224         public static final int VolumeListResult               = 110;
225         public static final int AsecListResult                 = 111;
226         public static final int StorageUsersListResult         = 112;
227         public static final int CryptfsGetfieldResult          = 113;
228 
229         /*
230          * 200 series - Requestion action has been successfully completed.
231          */
232         public static final int ShareStatusResult              = 210;
233         public static final int AsecPathResult                 = 211;
234         public static final int ShareEnabledResult             = 212;
235 
236         /*
237          * 400 series - Command was accepted, but the requested action
238          *              did not take place.
239          */
240         public static final int OpFailedNoMedia                = 401;
241         public static final int OpFailedMediaBlank             = 402;
242         public static final int OpFailedMediaCorrupt           = 403;
243         public static final int OpFailedVolNotMounted          = 404;
244         public static final int OpFailedStorageBusy            = 405;
245         public static final int OpFailedStorageNotFound        = 406;
246 
247         /*
248          * 600 series - Unsolicited broadcasts.
249          */
250         public static final int DISK_CREATED = 640;
251         public static final int DISK_SIZE_CHANGED = 641;
252         public static final int DISK_LABEL_CHANGED = 642;
253         public static final int DISK_SCANNED = 643;
254         public static final int DISK_SYS_PATH_CHANGED = 644;
255         public static final int DISK_DESTROYED = 649;
256 
257         public static final int VOLUME_CREATED = 650;
258         public static final int VOLUME_STATE_CHANGED = 651;
259         public static final int VOLUME_FS_TYPE_CHANGED = 652;
260         public static final int VOLUME_FS_UUID_CHANGED = 653;
261         public static final int VOLUME_FS_LABEL_CHANGED = 654;
262         public static final int VOLUME_PATH_CHANGED = 655;
263         public static final int VOLUME_INTERNAL_PATH_CHANGED = 656;
264         public static final int VOLUME_DESTROYED = 659;
265 
266         public static final int MOVE_STATUS = 660;
267         public static final int BENCHMARK_RESULT = 661;
268         public static final int TRIM_RESULT = 662;
269     }
270 
271     private static final int VERSION_INIT = 1;
272     private static final int VERSION_ADD_PRIMARY = 2;
273     private static final int VERSION_FIX_PRIMARY = 3;
274 
275     private static final String TAG_VOLUMES = "volumes";
276     private static final String ATTR_VERSION = "version";
277     private static final String ATTR_PRIMARY_STORAGE_UUID = "primaryStorageUuid";
278     private static final String ATTR_FORCE_ADOPTABLE = "forceAdoptable";
279     private static final String TAG_VOLUME = "volume";
280     private static final String ATTR_TYPE = "type";
281     private static final String ATTR_FS_UUID = "fsUuid";
282     private static final String ATTR_PART_GUID = "partGuid";
283     private static final String ATTR_NICKNAME = "nickname";
284     private static final String ATTR_USER_FLAGS = "userFlags";
285     private static final String ATTR_CREATED_MILLIS = "createdMillis";
286     private static final String ATTR_LAST_TRIM_MILLIS = "lastTrimMillis";
287     private static final String ATTR_LAST_BENCH_MILLIS = "lastBenchMillis";
288 
289     private final AtomicFile mSettingsFile;
290 
291     /**
292      * <em>Never</em> hold the lock while performing downcalls into vold, since
293      * unsolicited events can suddenly appear to update data structures.
294      */
295     private final Object mLock = new Object();
296 
297     /** Set of users that we know are unlocked. */
298     @GuardedBy("mLock")
299     private int[] mLocalUnlockedUsers = EmptyArray.INT;
300     /** Set of users that system knows are unlocked. */
301     @GuardedBy("mLock")
302     private int[] mSystemUnlockedUsers = EmptyArray.INT;
303 
304     /** Map from disk ID to disk */
305     @GuardedBy("mLock")
306     private ArrayMap<String, DiskInfo> mDisks = new ArrayMap<>();
307     /** Map from volume ID to disk */
308     @GuardedBy("mLock")
309     private final ArrayMap<String, VolumeInfo> mVolumes = new ArrayMap<>();
310 
311     /** Map from UUID to record */
312     @GuardedBy("mLock")
313     private ArrayMap<String, VolumeRecord> mRecords = new ArrayMap<>();
314     @GuardedBy("mLock")
315     private String mPrimaryStorageUuid;
316     @GuardedBy("mLock")
317     private boolean mForceAdoptable;
318 
319     /** Map from disk ID to latches */
320     @GuardedBy("mLock")
321     private ArrayMap<String, CountDownLatch> mDiskScanLatches = new ArrayMap<>();
322 
323     @GuardedBy("mLock")
324     private IPackageMoveObserver mMoveCallback;
325     @GuardedBy("mLock")
326     private String mMoveTargetUuid;
327 
328     private volatile int mCurrentUserId = UserHandle.USER_SYSTEM;
329 
findVolumeByIdOrThrow(String id)330     private VolumeInfo findVolumeByIdOrThrow(String id) {
331         synchronized (mLock) {
332             final VolumeInfo vol = mVolumes.get(id);
333             if (vol != null) {
334                 return vol;
335             }
336         }
337         throw new IllegalArgumentException("No volume found for ID " + id);
338     }
339 
findVolumeIdForPathOrThrow(String path)340     private String findVolumeIdForPathOrThrow(String path) {
341         synchronized (mLock) {
342             for (int i = 0; i < mVolumes.size(); i++) {
343                 final VolumeInfo vol = mVolumes.valueAt(i);
344                 if (vol.path != null && path.startsWith(vol.path)) {
345                     return vol.id;
346                 }
347             }
348         }
349         throw new IllegalArgumentException("No volume found for path " + path);
350     }
351 
findRecordForPath(String path)352     private VolumeRecord findRecordForPath(String path) {
353         synchronized (mLock) {
354             for (int i = 0; i < mVolumes.size(); i++) {
355                 final VolumeInfo vol = mVolumes.valueAt(i);
356                 if (vol.path != null && path.startsWith(vol.path)) {
357                     return mRecords.get(vol.fsUuid);
358                 }
359             }
360         }
361         return null;
362     }
363 
scrubPath(String path)364     private String scrubPath(String path) {
365         if (path.startsWith(Environment.getDataDirectory().getAbsolutePath())) {
366             return "internal";
367         }
368         final VolumeRecord rec = findRecordForPath(path);
369         if (rec == null || rec.createdMillis == 0) {
370             return "unknown";
371         } else {
372             return "ext:" + (int) ((System.currentTimeMillis() - rec.createdMillis)
373                     / DateUtils.WEEK_IN_MILLIS) + "w";
374         }
375     }
376 
findStorageForUuid(String volumeUuid)377     private @Nullable VolumeInfo findStorageForUuid(String volumeUuid) {
378         final StorageManager storage = mContext.getSystemService(StorageManager.class);
379         if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) {
380             return storage.findVolumeById(VolumeInfo.ID_EMULATED_INTERNAL);
381         } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
382             return storage.getPrimaryPhysicalVolume();
383         } else {
384             return storage.findEmulatedForPrivate(storage.findVolumeByUuid(volumeUuid));
385         }
386     }
387 
shouldBenchmark()388     private boolean shouldBenchmark() {
389         final long benchInterval = Settings.Global.getLong(mContext.getContentResolver(),
390                 Settings.Global.STORAGE_BENCHMARK_INTERVAL, DateUtils.WEEK_IN_MILLIS);
391         if (benchInterval == -1) {
392             return false;
393         } else if (benchInterval == 0) {
394             return true;
395         }
396 
397         synchronized (mLock) {
398             for (int i = 0; i < mVolumes.size(); i++) {
399                 final VolumeInfo vol = mVolumes.valueAt(i);
400                 final VolumeRecord rec = mRecords.get(vol.fsUuid);
401                 if (vol.isMountedWritable() && rec != null) {
402                     final long benchAge = System.currentTimeMillis() - rec.lastBenchMillis;
403                     if (benchAge >= benchInterval) {
404                         return true;
405                     }
406                 }
407             }
408             return false;
409         }
410     }
411 
findOrCreateDiskScanLatch(String diskId)412     private CountDownLatch findOrCreateDiskScanLatch(String diskId) {
413         synchronized (mLock) {
414             CountDownLatch latch = mDiskScanLatches.get(diskId);
415             if (latch == null) {
416                 latch = new CountDownLatch(1);
417                 mDiskScanLatches.put(diskId, latch);
418             }
419             return latch;
420         }
421     }
422 
escapeNull(String arg)423     private static String escapeNull(String arg) {
424         if (TextUtils.isEmpty(arg)) {
425             return "!";
426         } else {
427             if (arg.indexOf('\0') != -1 || arg.indexOf(' ') != -1) {
428                 throw new IllegalArgumentException(arg);
429             }
430             return arg;
431         }
432     }
433 
434     /** List of crypto types.
435       * These must match CRYPT_TYPE_XXX in cryptfs.h AND their
436       * corresponding commands in CommandListener.cpp */
437     public static final String[] CRYPTO_TYPES
438         = { "password", "default", "pattern", "pin" };
439 
440     private final Context mContext;
441 
442     private final NativeDaemonConnector mConnector;
443     private final NativeDaemonConnector mCryptConnector;
444 
445     private final Thread mConnectorThread;
446     private final Thread mCryptConnectorThread;
447 
448     private volatile boolean mSystemReady = false;
449     private volatile boolean mBootCompleted = false;
450     private volatile boolean mDaemonConnected = false;
451 
452     private PackageManagerService mPms;
453 
454     private final Callbacks mCallbacks;
455     private final LockPatternUtils mLockPatternUtils;
456 
457     // Two connectors - mConnector & mCryptConnector
458     private final CountDownLatch mConnectedSignal = new CountDownLatch(2);
459     private final CountDownLatch mAsecsScanned = new CountDownLatch(1);
460 
461     private final Object mUnmountLock = new Object();
462     @GuardedBy("mUnmountLock")
463     private CountDownLatch mUnmountSignal;
464 
465     /**
466      * Private hash of currently mounted secure containers.
467      * Used as a lock in methods to manipulate secure containers.
468      */
469     final private HashSet<String> mAsecMountSet = new HashSet<String>();
470 
471     /**
472      * The size of the crypto algorithm key in bits for OBB files. Currently
473      * Twofish is used which takes 128-bit keys.
474      */
475     private static final int CRYPTO_ALGORITHM_KEY_SIZE = 128;
476 
477     /**
478      * The number of times to run SHA1 in the PBKDF2 function for OBB files.
479      * 1024 is reasonably secure and not too slow.
480      */
481     private static final int PBKDF2_HASH_ROUNDS = 1024;
482 
483     /**
484      * Mounted OBB tracking information. Used to track the current state of all
485      * OBBs.
486      */
487     final private Map<IBinder, List<ObbState>> mObbMounts = new HashMap<IBinder, List<ObbState>>();
488 
489     /** Map from raw paths to {@link ObbState}. */
490     final private Map<String, ObbState> mObbPathToStateMap = new HashMap<String, ObbState>();
491 
492     // Not guarded by a lock.
493     private final MountServiceInternalImpl mMountServiceInternal = new MountServiceInternalImpl();
494 
495     class ObbState implements IBinder.DeathRecipient {
ObbState(String rawPath, String canonicalPath, int callingUid, IObbActionListener token, int nonce)496         public ObbState(String rawPath, String canonicalPath, int callingUid,
497                 IObbActionListener token, int nonce) {
498             this.rawPath = rawPath;
499             this.canonicalPath = canonicalPath;
500 
501             this.ownerGid = UserHandle.getSharedAppGid(callingUid);
502             this.token = token;
503             this.nonce = nonce;
504         }
505 
506         final String rawPath;
507         final String canonicalPath;
508 
509         final int ownerGid;
510 
511         // Token of remote Binder caller
512         final IObbActionListener token;
513 
514         // Identifier to pass back to the token
515         final int nonce;
516 
getBinder()517         public IBinder getBinder() {
518             return token.asBinder();
519         }
520 
521         @Override
binderDied()522         public void binderDied() {
523             ObbAction action = new UnmountObbAction(this, true);
524             mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
525         }
526 
link()527         public void link() throws RemoteException {
528             getBinder().linkToDeath(this, 0);
529         }
530 
unlink()531         public void unlink() {
532             getBinder().unlinkToDeath(this, 0);
533         }
534 
535         @Override
toString()536         public String toString() {
537             StringBuilder sb = new StringBuilder("ObbState{");
538             sb.append("rawPath=").append(rawPath);
539             sb.append(",canonicalPath=").append(canonicalPath);
540             sb.append(",ownerGid=").append(ownerGid);
541             sb.append(",token=").append(token);
542             sb.append(",binder=").append(getBinder());
543             sb.append('}');
544             return sb.toString();
545         }
546     }
547 
548     // OBB Action Handler
549     final private ObbActionHandler mObbActionHandler;
550 
551     // OBB action handler messages
552     private static final int OBB_RUN_ACTION = 1;
553     private static final int OBB_MCS_BOUND = 2;
554     private static final int OBB_MCS_UNBIND = 3;
555     private static final int OBB_MCS_RECONNECT = 4;
556     private static final int OBB_FLUSH_MOUNT_STATE = 5;
557 
558     /*
559      * Default Container Service information
560      */
561     static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
562             "com.android.defcontainer", "com.android.defcontainer.DefaultContainerService");
563 
564     final private DefaultContainerConnection mDefContainerConn = new DefaultContainerConnection();
565 
566     class DefaultContainerConnection implements ServiceConnection {
567         @Override
onServiceConnected(ComponentName name, IBinder service)568         public void onServiceConnected(ComponentName name, IBinder service) {
569             if (DEBUG_OBB)
570                 Slog.i(TAG, "onServiceConnected");
571             IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(service);
572             mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_MCS_BOUND, imcs));
573         }
574 
575         @Override
onServiceDisconnected(ComponentName name)576         public void onServiceDisconnected(ComponentName name) {
577             if (DEBUG_OBB)
578                 Slog.i(TAG, "onServiceDisconnected");
579         }
580     };
581 
582     // Used in the ObbActionHandler
583     private IMediaContainerService mContainerService = null;
584 
585     // Last fstrim operation tracking
586     private static final String LAST_FSTRIM_FILE = "last-fstrim";
587     private final File mLastMaintenanceFile;
588     private long mLastMaintenance;
589 
590     // Handler messages
591     private static final int H_SYSTEM_READY = 1;
592     private static final int H_DAEMON_CONNECTED = 2;
593     private static final int H_SHUTDOWN = 3;
594     private static final int H_FSTRIM = 4;
595     private static final int H_VOLUME_MOUNT = 5;
596     private static final int H_VOLUME_BROADCAST = 6;
597     private static final int H_INTERNAL_BROADCAST = 7;
598     private static final int H_VOLUME_UNMOUNT = 8;
599     private static final int H_PARTITION_FORGET = 9;
600     private static final int H_RESET = 10;
601 
602     class MountServiceHandler extends Handler {
MountServiceHandler(Looper looper)603         public MountServiceHandler(Looper looper) {
604             super(looper);
605         }
606 
607         @Override
handleMessage(Message msg)608         public void handleMessage(Message msg) {
609             switch (msg.what) {
610                 case H_SYSTEM_READY: {
611                     handleSystemReady();
612                     break;
613                 }
614                 case H_DAEMON_CONNECTED: {
615                     handleDaemonConnected();
616                     break;
617                 }
618                 case H_FSTRIM: {
619                     if (!isReady()) {
620                         Slog.i(TAG, "fstrim requested, but no daemon connection yet; trying again");
621                         sendMessageDelayed(obtainMessage(H_FSTRIM, msg.obj),
622                                 DateUtils.SECOND_IN_MILLIS);
623                         break;
624                     }
625 
626                     Slog.i(TAG, "Running fstrim idle maintenance");
627 
628                     // Remember when we kicked it off
629                     try {
630                         mLastMaintenance = System.currentTimeMillis();
631                         mLastMaintenanceFile.setLastModified(mLastMaintenance);
632                     } catch (Exception e) {
633                         Slog.e(TAG, "Unable to record last fstrim!");
634                     }
635 
636                     final boolean shouldBenchmark = shouldBenchmark();
637                     try {
638                         // This method must be run on the main (handler) thread,
639                         // so it is safe to directly call into vold.
640                         mConnector.execute("fstrim", shouldBenchmark ? "dotrimbench" : "dotrim");
641                     } catch (NativeDaemonConnectorException ndce) {
642                         Slog.e(TAG, "Failed to run fstrim!");
643                     }
644 
645                     // invoke the completion callback, if any
646                     // TODO: fstrim is non-blocking, so remove this useless callback
647                     Runnable callback = (Runnable) msg.obj;
648                     if (callback != null) {
649                         callback.run();
650                     }
651                     break;
652                 }
653                 case H_SHUTDOWN: {
654                     final IMountShutdownObserver obs = (IMountShutdownObserver) msg.obj;
655                     boolean success = false;
656                     try {
657                         success = mConnector.execute("volume", "shutdown").isClassOk();
658                     } catch (NativeDaemonConnectorException ignored) {
659                     }
660                     if (obs != null) {
661                         try {
662                             obs.onShutDownComplete(success ? 0 : -1);
663                         } catch (RemoteException ignored) {
664                         }
665                     }
666                     break;
667                 }
668                 case H_VOLUME_MOUNT: {
669                     final VolumeInfo vol = (VolumeInfo) msg.obj;
670                     if (isMountDisallowed(vol)) {
671                         Slog.i(TAG, "Ignoring mount " + vol.getId() + " due to policy");
672                         break;
673                     }
674                     try {
675                         mConnector.execute("volume", "mount", vol.id, vol.mountFlags,
676                                 vol.mountUserId);
677                     } catch (NativeDaemonConnectorException ignored) {
678                     }
679                     break;
680                 }
681                 case H_VOLUME_UNMOUNT: {
682                     final VolumeInfo vol = (VolumeInfo) msg.obj;
683                     unmount(vol.getId());
684                     break;
685                 }
686                 case H_VOLUME_BROADCAST: {
687                     final StorageVolume userVol = (StorageVolume) msg.obj;
688                     final String envState = userVol.getState();
689                     Slog.d(TAG, "Volume " + userVol.getId() + " broadcasting " + envState + " to "
690                             + userVol.getOwner());
691 
692                     final String action = VolumeInfo.getBroadcastForEnvironment(envState);
693                     if (action != null) {
694                         final Intent intent = new Intent(action,
695                                 Uri.fromFile(userVol.getPathFile()));
696                         intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, userVol);
697                         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
698                         mContext.sendBroadcastAsUser(intent, userVol.getOwner());
699                     }
700                     break;
701                 }
702                 case H_INTERNAL_BROADCAST: {
703                     // Internal broadcasts aimed at system components, not for
704                     // third-party apps.
705                     final Intent intent = (Intent) msg.obj;
706                     mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
707                             android.Manifest.permission.WRITE_MEDIA_STORAGE);
708                     break;
709                 }
710                 case H_PARTITION_FORGET: {
711                     final String partGuid = (String) msg.obj;
712                     forgetPartition(partGuid);
713                     break;
714                 }
715                 case H_RESET: {
716                     resetIfReadyAndConnected();
717                     break;
718                 }
719             }
720         }
721     }
722 
723     private final Handler mHandler;
724 
725     private BroadcastReceiver mUserReceiver = new BroadcastReceiver() {
726         @Override
727         public void onReceive(Context context, Intent intent) {
728             final String action = intent.getAction();
729             final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
730             Preconditions.checkArgument(userId >= 0);
731 
732             try {
733                 if (Intent.ACTION_USER_ADDED.equals(action)) {
734                     final UserManager um = mContext.getSystemService(UserManager.class);
735                     final int userSerialNumber = um.getUserSerialNumber(userId);
736                     mConnector.execute("volume", "user_added", userId, userSerialNumber);
737                 } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
738                     synchronized (mVolumes) {
739                         final int size = mVolumes.size();
740                         for (int i = 0; i < size; i++) {
741                             final VolumeInfo vol = mVolumes.valueAt(i);
742                             if (vol.mountUserId == userId) {
743                                 vol.mountUserId = UserHandle.USER_NULL;
744                                 mHandler.obtainMessage(H_VOLUME_UNMOUNT, vol).sendToTarget();
745                             }
746                         }
747                     }
748                     mConnector.execute("volume", "user_removed", userId);
749                 }
750             } catch (NativeDaemonConnectorException e) {
751                 Slog.w(TAG, "Failed to send user details to vold", e);
752             }
753         }
754     };
755 
756     @Override
waitForAsecScan()757     public void waitForAsecScan() {
758         waitForLatch(mAsecsScanned, "mAsecsScanned");
759     }
760 
waitForReady()761     private void waitForReady() {
762         waitForLatch(mConnectedSignal, "mConnectedSignal");
763     }
764 
waitForLatch(CountDownLatch latch, String condition)765     private void waitForLatch(CountDownLatch latch, String condition) {
766         try {
767             waitForLatch(latch, condition, -1);
768         } catch (TimeoutException ignored) {
769         }
770     }
771 
waitForLatch(CountDownLatch latch, String condition, long timeoutMillis)772     private void waitForLatch(CountDownLatch latch, String condition, long timeoutMillis)
773             throws TimeoutException {
774         final long startMillis = SystemClock.elapsedRealtime();
775         while (true) {
776             try {
777                 if (latch.await(5000, TimeUnit.MILLISECONDS)) {
778                     return;
779                 } else {
780                     Slog.w(TAG, "Thread " + Thread.currentThread().getName()
781                             + " still waiting for " + condition + "...");
782                 }
783             } catch (InterruptedException e) {
784                 Slog.w(TAG, "Interrupt while waiting for " + condition);
785             }
786             if (timeoutMillis > 0 && SystemClock.elapsedRealtime() > startMillis + timeoutMillis) {
787                 throw new TimeoutException("Thread " + Thread.currentThread().getName()
788                         + " gave up waiting for " + condition + " after " + timeoutMillis + "ms");
789             }
790         }
791     }
792 
isReady()793     private boolean isReady() {
794         try {
795             return mConnectedSignal.await(0, TimeUnit.MILLISECONDS);
796         } catch (InterruptedException e) {
797             return false;
798         }
799     }
800 
handleSystemReady()801     private void handleSystemReady() {
802         initIfReadyAndConnected();
803         resetIfReadyAndConnected();
804 
805         // Start scheduling nominally-daily fstrim operations
806         MountServiceIdler.scheduleIdlePass(mContext);
807     }
808 
809     /**
810      * MediaProvider has a ton of code that makes assumptions about storage
811      * paths never changing, so we outright kill them to pick up new state.
812      */
813     @Deprecated
killMediaProvider(List<UserInfo> users)814     private void killMediaProvider(List<UserInfo> users) {
815         if (users == null) return;
816 
817         final long token = Binder.clearCallingIdentity();
818         try {
819             for (UserInfo user : users) {
820                 // System user does not have media provider, so skip.
821                 if (user.isSystemOnly()) continue;
822 
823                 final ProviderInfo provider = mPms.resolveContentProvider(MediaStore.AUTHORITY,
824                         PackageManager.MATCH_DIRECT_BOOT_AWARE
825                                 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
826                         user.id);
827                 if (provider != null) {
828                     final IActivityManager am = ActivityManagerNative.getDefault();
829                     try {
830                         am.killApplication(provider.applicationInfo.packageName,
831                                 UserHandle.getAppId(provider.applicationInfo.uid),
832                                 UserHandle.USER_ALL, "vold reset");
833                         // We only need to run this once. It will kill all users' media processes.
834                         break;
835                     } catch (RemoteException e) {
836                     }
837                 }
838             }
839         } finally {
840             Binder.restoreCallingIdentity(token);
841         }
842     }
843 
addInternalVolumeLocked()844     private void addInternalVolumeLocked() {
845         // Create a stub volume that represents internal storage
846         final VolumeInfo internal = new VolumeInfo(VolumeInfo.ID_PRIVATE_INTERNAL,
847                 VolumeInfo.TYPE_PRIVATE, null, null);
848         internal.state = VolumeInfo.STATE_MOUNTED;
849         internal.path = Environment.getDataDirectory().getAbsolutePath();
850         mVolumes.put(internal.id, internal);
851     }
852 
initIfReadyAndConnected()853     private void initIfReadyAndConnected() {
854         Slog.d(TAG, "Thinking about init, mSystemReady=" + mSystemReady
855                 + ", mDaemonConnected=" + mDaemonConnected);
856         if (mSystemReady && mDaemonConnected
857                 && !StorageManager.isFileEncryptedNativeOnly()) {
858             // When booting a device without native support, make sure that our
859             // user directories are locked or unlocked based on the current
860             // emulation status.
861             final boolean initLocked = StorageManager.isFileEncryptedEmulatedOnly();
862             Slog.d(TAG, "Setting up emulation state, initlocked=" + initLocked);
863             final List<UserInfo> users = mContext.getSystemService(UserManager.class).getUsers();
864             for (UserInfo user : users) {
865                 try {
866                     if (initLocked) {
867                         mCryptConnector.execute("cryptfs", "lock_user_key", user.id);
868                     } else {
869                         mCryptConnector.execute("cryptfs", "unlock_user_key", user.id,
870                                 user.serialNumber, "!", "!");
871                     }
872                 } catch (NativeDaemonConnectorException e) {
873                     Slog.w(TAG, "Failed to init vold", e);
874                 }
875             }
876         }
877     }
878 
resetIfReadyAndConnected()879     private void resetIfReadyAndConnected() {
880         Slog.d(TAG, "Thinking about reset, mSystemReady=" + mSystemReady
881                 + ", mDaemonConnected=" + mDaemonConnected);
882         if (mSystemReady && mDaemonConnected) {
883             final List<UserInfo> users = mContext.getSystemService(UserManager.class).getUsers();
884             killMediaProvider(users);
885 
886             final int[] systemUnlockedUsers;
887             synchronized (mLock) {
888                 systemUnlockedUsers = mSystemUnlockedUsers;
889 
890                 mDisks.clear();
891                 mVolumes.clear();
892 
893                 addInternalVolumeLocked();
894             }
895 
896             try {
897                 mConnector.execute("volume", "reset");
898 
899                 // Tell vold about all existing and started users
900                 for (UserInfo user : users) {
901                     mConnector.execute("volume", "user_added", user.id, user.serialNumber);
902                 }
903                 for (int userId : systemUnlockedUsers) {
904                     mConnector.execute("volume", "user_started", userId);
905                 }
906             } catch (NativeDaemonConnectorException e) {
907                 Slog.w(TAG, "Failed to reset vold", e);
908             }
909         }
910     }
911 
onUnlockUser(int userId)912     private void onUnlockUser(int userId) {
913         Slog.d(TAG, "onUnlockUser " + userId);
914 
915         // We purposefully block here to make sure that user-specific
916         // staging area is ready so it's ready for zygote-forked apps to
917         // bind mount against.
918         try {
919             mConnector.execute("volume", "user_started", userId);
920         } catch (NativeDaemonConnectorException ignored) {
921         }
922 
923         // Record user as started so newly mounted volumes kick off events
924         // correctly, then synthesize events for any already-mounted volumes.
925         synchronized (mVolumes) {
926             for (int i = 0; i < mVolumes.size(); i++) {
927                 final VolumeInfo vol = mVolumes.valueAt(i);
928                 if (vol.isVisibleForRead(userId) && vol.isMountedReadable()) {
929                     final StorageVolume userVol = vol.buildStorageVolume(mContext, userId, false);
930                     mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget();
931 
932                     final String envState = VolumeInfo.getEnvironmentForState(vol.getState());
933                     mCallbacks.notifyStorageStateChanged(userVol.getPath(), envState, envState);
934                 }
935             }
936             mSystemUnlockedUsers = ArrayUtils.appendInt(mSystemUnlockedUsers, userId);
937         }
938     }
939 
onCleanupUser(int userId)940     private void onCleanupUser(int userId) {
941         Slog.d(TAG, "onCleanupUser " + userId);
942 
943         try {
944             mConnector.execute("volume", "user_stopped", userId);
945         } catch (NativeDaemonConnectorException ignored) {
946         }
947 
948         synchronized (mVolumes) {
949             mSystemUnlockedUsers = ArrayUtils.removeInt(mSystemUnlockedUsers, userId);
950         }
951     }
952 
runIdleMaintenance(Runnable callback)953     void runIdleMaintenance(Runnable callback) {
954         mHandler.sendMessage(mHandler.obtainMessage(H_FSTRIM, callback));
955     }
956 
957     // Binder entry point for kicking off an immediate fstrim
958     @Override
runMaintenance()959     public void runMaintenance() {
960         enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
961         runIdleMaintenance(null);
962     }
963 
964     @Override
lastMaintenance()965     public long lastMaintenance() {
966         return mLastMaintenance;
967     }
968 
969     /**
970      * Callback from NativeDaemonConnector
971      */
972     @Override
onDaemonConnected()973     public void onDaemonConnected() {
974         mDaemonConnected = true;
975         mHandler.obtainMessage(H_DAEMON_CONNECTED).sendToTarget();
976     }
977 
handleDaemonConnected()978     private void handleDaemonConnected() {
979         initIfReadyAndConnected();
980         resetIfReadyAndConnected();
981 
982         /*
983          * Now that we've done our initialization, release
984          * the hounds!
985          */
986         mConnectedSignal.countDown();
987         if (mConnectedSignal.getCount() != 0) {
988             // More daemons need to connect
989             return;
990         }
991 
992         // On an encrypted device we can't see system properties yet, so pull
993         // the system locale out of the mount service.
994         if ("".equals(SystemProperties.get("vold.encrypt_progress"))) {
995             copyLocaleFromMountService();
996         }
997 
998         // Let package manager load internal ASECs.
999         mPms.scanAvailableAsecs();
1000 
1001         // Notify people waiting for ASECs to be scanned that it's done.
1002         mAsecsScanned.countDown();
1003     }
1004 
copyLocaleFromMountService()1005     private void copyLocaleFromMountService() {
1006         String systemLocale;
1007         try {
1008             systemLocale = getField(StorageManager.SYSTEM_LOCALE_KEY);
1009         } catch (RemoteException e) {
1010             return;
1011         }
1012         if (TextUtils.isEmpty(systemLocale)) {
1013             return;
1014         }
1015 
1016         Slog.d(TAG, "Got locale " + systemLocale + " from mount service");
1017         Locale locale = Locale.forLanguageTag(systemLocale);
1018         Configuration config = new Configuration();
1019         config.setLocale(locale);
1020         try {
1021             ActivityManagerNative.getDefault().updatePersistentConfiguration(config);
1022         } catch (RemoteException e) {
1023             Slog.e(TAG, "Error setting system locale from mount service", e);
1024         }
1025 
1026         // Temporary workaround for http://b/17945169.
1027         Slog.d(TAG, "Setting system properties to " + systemLocale + " from mount service");
1028         SystemProperties.set("persist.sys.locale", locale.toLanguageTag());
1029     }
1030 
1031     /**
1032      * Callback from NativeDaemonConnector
1033      */
1034     @Override
onCheckHoldWakeLock(int code)1035     public boolean onCheckHoldWakeLock(int code) {
1036         return false;
1037     }
1038 
1039     /**
1040      * Callback from NativeDaemonConnector
1041      */
1042     @Override
onEvent(int code, String raw, String[] cooked)1043     public boolean onEvent(int code, String raw, String[] cooked) {
1044         synchronized (mLock) {
1045             return onEventLocked(code, raw, cooked);
1046         }
1047     }
1048 
onEventLocked(int code, String raw, String[] cooked)1049     private boolean onEventLocked(int code, String raw, String[] cooked) {
1050         switch (code) {
1051             case VoldResponseCode.DISK_CREATED: {
1052                 if (cooked.length != 3) break;
1053                 final String id = cooked[1];
1054                 int flags = Integer.parseInt(cooked[2]);
1055                 if (SystemProperties.getBoolean(StorageManager.PROP_FORCE_ADOPTABLE, false)
1056                         || mForceAdoptable) {
1057                     flags |= DiskInfo.FLAG_ADOPTABLE;
1058                 }
1059                 mDisks.put(id, new DiskInfo(id, flags));
1060                 break;
1061             }
1062             case VoldResponseCode.DISK_SIZE_CHANGED: {
1063                 if (cooked.length != 3) break;
1064                 final DiskInfo disk = mDisks.get(cooked[1]);
1065                 if (disk != null) {
1066                     disk.size = Long.parseLong(cooked[2]);
1067                 }
1068                 break;
1069             }
1070             case VoldResponseCode.DISK_LABEL_CHANGED: {
1071                 final DiskInfo disk = mDisks.get(cooked[1]);
1072                 if (disk != null) {
1073                     final StringBuilder builder = new StringBuilder();
1074                     for (int i = 2; i < cooked.length; i++) {
1075                         builder.append(cooked[i]).append(' ');
1076                     }
1077                     disk.label = builder.toString().trim();
1078                 }
1079                 break;
1080             }
1081             case VoldResponseCode.DISK_SCANNED: {
1082                 if (cooked.length != 2) break;
1083                 final DiskInfo disk = mDisks.get(cooked[1]);
1084                 if (disk != null) {
1085                     onDiskScannedLocked(disk);
1086                 }
1087                 break;
1088             }
1089             case VoldResponseCode.DISK_SYS_PATH_CHANGED: {
1090                 if (cooked.length != 3) break;
1091                 final DiskInfo disk = mDisks.get(cooked[1]);
1092                 if (disk != null) {
1093                     disk.sysPath = cooked[2];
1094                 }
1095                 break;
1096             }
1097             case VoldResponseCode.DISK_DESTROYED: {
1098                 if (cooked.length != 2) break;
1099                 final DiskInfo disk = mDisks.remove(cooked[1]);
1100                 if (disk != null) {
1101                     mCallbacks.notifyDiskDestroyed(disk);
1102                 }
1103                 break;
1104             }
1105 
1106             case VoldResponseCode.VOLUME_CREATED: {
1107                 final String id = cooked[1];
1108                 final int type = Integer.parseInt(cooked[2]);
1109                 final String diskId = TextUtils.nullIfEmpty(cooked[3]);
1110                 final String partGuid = TextUtils.nullIfEmpty(cooked[4]);
1111 
1112                 final DiskInfo disk = mDisks.get(diskId);
1113                 final VolumeInfo vol = new VolumeInfo(id, type, disk, partGuid);
1114                 mVolumes.put(id, vol);
1115                 onVolumeCreatedLocked(vol);
1116                 break;
1117             }
1118             case VoldResponseCode.VOLUME_STATE_CHANGED: {
1119                 if (cooked.length != 3) break;
1120                 final VolumeInfo vol = mVolumes.get(cooked[1]);
1121                 if (vol != null) {
1122                     final int oldState = vol.state;
1123                     final int newState = Integer.parseInt(cooked[2]);
1124                     vol.state = newState;
1125                     onVolumeStateChangedLocked(vol, oldState, newState);
1126                 }
1127                 break;
1128             }
1129             case VoldResponseCode.VOLUME_FS_TYPE_CHANGED: {
1130                 if (cooked.length != 3) break;
1131                 final VolumeInfo vol = mVolumes.get(cooked[1]);
1132                 if (vol != null) {
1133                     vol.fsType = cooked[2];
1134                 }
1135                 break;
1136             }
1137             case VoldResponseCode.VOLUME_FS_UUID_CHANGED: {
1138                 if (cooked.length != 3) break;
1139                 final VolumeInfo vol = mVolumes.get(cooked[1]);
1140                 if (vol != null) {
1141                     vol.fsUuid = cooked[2];
1142                 }
1143                 break;
1144             }
1145             case VoldResponseCode.VOLUME_FS_LABEL_CHANGED: {
1146                 final VolumeInfo vol = mVolumes.get(cooked[1]);
1147                 if (vol != null) {
1148                     final StringBuilder builder = new StringBuilder();
1149                     for (int i = 2; i < cooked.length; i++) {
1150                         builder.append(cooked[i]).append(' ');
1151                     }
1152                     vol.fsLabel = builder.toString().trim();
1153                 }
1154                 // TODO: notify listeners that label changed
1155                 break;
1156             }
1157             case VoldResponseCode.VOLUME_PATH_CHANGED: {
1158                 if (cooked.length != 3) break;
1159                 final VolumeInfo vol = mVolumes.get(cooked[1]);
1160                 if (vol != null) {
1161                     vol.path = cooked[2];
1162                 }
1163                 break;
1164             }
1165             case VoldResponseCode.VOLUME_INTERNAL_PATH_CHANGED: {
1166                 if (cooked.length != 3) break;
1167                 final VolumeInfo vol = mVolumes.get(cooked[1]);
1168                 if (vol != null) {
1169                     vol.internalPath = cooked[2];
1170                 }
1171                 break;
1172             }
1173             case VoldResponseCode.VOLUME_DESTROYED: {
1174                 if (cooked.length != 2) break;
1175                 mVolumes.remove(cooked[1]);
1176                 break;
1177             }
1178 
1179             case VoldResponseCode.MOVE_STATUS: {
1180                 final int status = Integer.parseInt(cooked[1]);
1181                 onMoveStatusLocked(status);
1182                 break;
1183             }
1184             case VoldResponseCode.BENCHMARK_RESULT: {
1185                 if (cooked.length != 7) break;
1186                 final String path = cooked[1];
1187                 final String ident = cooked[2];
1188                 final long create = Long.parseLong(cooked[3]);
1189                 final long drop = Long.parseLong(cooked[4]);
1190                 final long run = Long.parseLong(cooked[5]);
1191                 final long destroy = Long.parseLong(cooked[6]);
1192 
1193                 final DropBoxManager dropBox = mContext.getSystemService(DropBoxManager.class);
1194                 dropBox.addText(TAG_STORAGE_BENCHMARK, scrubPath(path)
1195                         + " " + ident + " " + create + " " + run + " " + destroy);
1196 
1197                 final VolumeRecord rec = findRecordForPath(path);
1198                 if (rec != null) {
1199                     rec.lastBenchMillis = System.currentTimeMillis();
1200                     writeSettingsLocked();
1201                 }
1202 
1203                 break;
1204             }
1205             case VoldResponseCode.TRIM_RESULT: {
1206                 if (cooked.length != 4) break;
1207                 final String path = cooked[1];
1208                 final long bytes = Long.parseLong(cooked[2]);
1209                 final long time = Long.parseLong(cooked[3]);
1210 
1211                 final DropBoxManager dropBox = mContext.getSystemService(DropBoxManager.class);
1212                 dropBox.addText(TAG_STORAGE_TRIM, scrubPath(path)
1213                         + " " + bytes + " " + time);
1214 
1215                 final VolumeRecord rec = findRecordForPath(path);
1216                 if (rec != null) {
1217                     rec.lastTrimMillis = System.currentTimeMillis();
1218                     writeSettingsLocked();
1219                 }
1220 
1221                 break;
1222             }
1223 
1224             default: {
1225                 Slog.d(TAG, "Unhandled vold event " + code);
1226             }
1227         }
1228 
1229         return true;
1230     }
1231 
onDiskScannedLocked(DiskInfo disk)1232     private void onDiskScannedLocked(DiskInfo disk) {
1233         int volumeCount = 0;
1234         for (int i = 0; i < mVolumes.size(); i++) {
1235             final VolumeInfo vol = mVolumes.valueAt(i);
1236             if (Objects.equals(disk.id, vol.getDiskId())) {
1237                 volumeCount++;
1238             }
1239         }
1240 
1241         final Intent intent = new Intent(DiskInfo.ACTION_DISK_SCANNED);
1242         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
1243                 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
1244         intent.putExtra(DiskInfo.EXTRA_DISK_ID, disk.id);
1245         intent.putExtra(DiskInfo.EXTRA_VOLUME_COUNT, volumeCount);
1246         mHandler.obtainMessage(H_INTERNAL_BROADCAST, intent).sendToTarget();
1247 
1248         final CountDownLatch latch = mDiskScanLatches.remove(disk.id);
1249         if (latch != null) {
1250             latch.countDown();
1251         }
1252 
1253         disk.volumeCount = volumeCount;
1254         mCallbacks.notifyDiskScanned(disk, volumeCount);
1255     }
1256 
onVolumeCreatedLocked(VolumeInfo vol)1257     private void onVolumeCreatedLocked(VolumeInfo vol) {
1258         if (mPms.isOnlyCoreApps()) {
1259             Slog.d(TAG, "System booted in core-only mode; ignoring volume " + vol.getId());
1260             return;
1261         }
1262 
1263         if (vol.type == VolumeInfo.TYPE_EMULATED) {
1264             final StorageManager storage = mContext.getSystemService(StorageManager.class);
1265             final VolumeInfo privateVol = storage.findPrivateForEmulated(vol);
1266 
1267             if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, mPrimaryStorageUuid)
1268                     && VolumeInfo.ID_PRIVATE_INTERNAL.equals(privateVol.id)) {
1269                 Slog.v(TAG, "Found primary storage at " + vol);
1270                 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
1271                 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
1272                 mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
1273 
1274             } else if (Objects.equals(privateVol.fsUuid, mPrimaryStorageUuid)) {
1275                 Slog.v(TAG, "Found primary storage at " + vol);
1276                 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
1277                 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
1278                 mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
1279             }
1280 
1281         } else if (vol.type == VolumeInfo.TYPE_PUBLIC) {
1282             // TODO: only look at first public partition
1283             if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, mPrimaryStorageUuid)
1284                     && vol.disk.isDefaultPrimary()) {
1285                 Slog.v(TAG, "Found primary storage at " + vol);
1286                 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
1287                 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
1288             }
1289 
1290             // Adoptable public disks are visible to apps, since they meet
1291             // public API requirement of being in a stable location.
1292             if (vol.disk.isAdoptable()) {
1293                 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
1294             }
1295 
1296             vol.mountUserId = mCurrentUserId;
1297             mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
1298 
1299         } else if (vol.type == VolumeInfo.TYPE_PRIVATE) {
1300             mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
1301 
1302         } else {
1303             Slog.d(TAG, "Skipping automatic mounting of " + vol);
1304         }
1305     }
1306 
isBroadcastWorthy(VolumeInfo vol)1307     private boolean isBroadcastWorthy(VolumeInfo vol) {
1308         switch (vol.getType()) {
1309             case VolumeInfo.TYPE_PRIVATE:
1310             case VolumeInfo.TYPE_PUBLIC:
1311             case VolumeInfo.TYPE_EMULATED:
1312                 break;
1313             default:
1314                 return false;
1315         }
1316 
1317         switch (vol.getState()) {
1318             case VolumeInfo.STATE_MOUNTED:
1319             case VolumeInfo.STATE_MOUNTED_READ_ONLY:
1320             case VolumeInfo.STATE_EJECTING:
1321             case VolumeInfo.STATE_UNMOUNTED:
1322             case VolumeInfo.STATE_UNMOUNTABLE:
1323             case VolumeInfo.STATE_BAD_REMOVAL:
1324                 break;
1325             default:
1326                 return false;
1327         }
1328 
1329         return true;
1330     }
1331 
onVolumeStateChangedLocked(VolumeInfo vol, int oldState, int newState)1332     private void onVolumeStateChangedLocked(VolumeInfo vol, int oldState, int newState) {
1333         // Remember that we saw this volume so we're ready to accept user
1334         // metadata, or so we can annoy them when a private volume is ejected
1335         if (vol.isMountedReadable() && !TextUtils.isEmpty(vol.fsUuid)) {
1336             VolumeRecord rec = mRecords.get(vol.fsUuid);
1337             if (rec == null) {
1338                 rec = new VolumeRecord(vol.type, vol.fsUuid);
1339                 rec.partGuid = vol.partGuid;
1340                 rec.createdMillis = System.currentTimeMillis();
1341                 if (vol.type == VolumeInfo.TYPE_PRIVATE) {
1342                     rec.nickname = vol.disk.getDescription();
1343                 }
1344                 mRecords.put(rec.fsUuid, rec);
1345                 writeSettingsLocked();
1346             } else {
1347                 // Handle upgrade case where we didn't store partition GUID
1348                 if (TextUtils.isEmpty(rec.partGuid)) {
1349                     rec.partGuid = vol.partGuid;
1350                     writeSettingsLocked();
1351                 }
1352             }
1353         }
1354 
1355         mCallbacks.notifyVolumeStateChanged(vol, oldState, newState);
1356 
1357         // Do not broadcast before boot has completed to avoid launching the
1358         // processes that receive the intent unnecessarily.
1359         if (mBootCompleted && isBroadcastWorthy(vol)) {
1360             final Intent intent = new Intent(VolumeInfo.ACTION_VOLUME_STATE_CHANGED);
1361             intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.id);
1362             intent.putExtra(VolumeInfo.EXTRA_VOLUME_STATE, newState);
1363             intent.putExtra(VolumeRecord.EXTRA_FS_UUID, vol.fsUuid);
1364             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
1365                     | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
1366             mHandler.obtainMessage(H_INTERNAL_BROADCAST, intent).sendToTarget();
1367         }
1368 
1369         final String oldStateEnv = VolumeInfo.getEnvironmentForState(oldState);
1370         final String newStateEnv = VolumeInfo.getEnvironmentForState(newState);
1371 
1372         if (!Objects.equals(oldStateEnv, newStateEnv)) {
1373             // Kick state changed event towards all started users. Any users
1374             // started after this point will trigger additional
1375             // user-specific broadcasts.
1376             for (int userId : mSystemUnlockedUsers) {
1377                 if (vol.isVisibleForRead(userId)) {
1378                     final StorageVolume userVol = vol.buildStorageVolume(mContext, userId, false);
1379                     mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget();
1380 
1381                     mCallbacks.notifyStorageStateChanged(userVol.getPath(), oldStateEnv,
1382                             newStateEnv);
1383                 }
1384             }
1385         }
1386 
1387         if (vol.type == VolumeInfo.TYPE_PUBLIC && vol.state == VolumeInfo.STATE_EJECTING) {
1388             // TODO: this should eventually be handled by new ObbVolume state changes
1389             /*
1390              * Some OBBs might have been unmounted when this volume was
1391              * unmounted, so send a message to the handler to let it know to
1392              * remove those from the list of mounted OBBS.
1393              */
1394             mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(
1395                     OBB_FLUSH_MOUNT_STATE, vol.path));
1396         }
1397     }
1398 
onMoveStatusLocked(int status)1399     private void onMoveStatusLocked(int status) {
1400         if (mMoveCallback == null) {
1401             Slog.w(TAG, "Odd, status but no move requested");
1402             return;
1403         }
1404 
1405         // TODO: estimate remaining time
1406         try {
1407             mMoveCallback.onStatusChanged(-1, status, -1);
1408         } catch (RemoteException ignored) {
1409         }
1410 
1411         // We've finished copying and we're about to clean up old data, so
1412         // remember that move was successful if we get rebooted
1413         if (status == MOVE_STATUS_COPY_FINISHED) {
1414             Slog.d(TAG, "Move to " + mMoveTargetUuid + " copy phase finshed; persisting");
1415 
1416             mPrimaryStorageUuid = mMoveTargetUuid;
1417             writeSettingsLocked();
1418         }
1419 
1420         if (PackageManager.isMoveStatusFinished(status)) {
1421             Slog.d(TAG, "Move to " + mMoveTargetUuid + " finished with status " + status);
1422 
1423             mMoveCallback = null;
1424             mMoveTargetUuid = null;
1425         }
1426     }
1427 
enforcePermission(String perm)1428     private void enforcePermission(String perm) {
1429         mContext.enforceCallingOrSelfPermission(perm, perm);
1430     }
1431 
1432     /**
1433      * Decide if volume is mountable per device policies.
1434      */
isMountDisallowed(VolumeInfo vol)1435     private boolean isMountDisallowed(VolumeInfo vol) {
1436         if (vol.type == VolumeInfo.TYPE_PUBLIC || vol.type == VolumeInfo.TYPE_PRIVATE) {
1437             final UserManager userManager = mContext.getSystemService(UserManager.class);
1438             return userManager.hasUserRestriction(UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA,
1439                     Binder.getCallingUserHandle());
1440         } else {
1441             return false;
1442         }
1443     }
1444 
enforceAdminUser()1445     private void enforceAdminUser() {
1446         UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
1447         final int callingUserId = UserHandle.getCallingUserId();
1448         boolean isAdmin;
1449         long token = Binder.clearCallingIdentity();
1450         try {
1451             isAdmin = um.getUserInfo(callingUserId).isAdmin();
1452         } finally {
1453             Binder.restoreCallingIdentity(token);
1454         }
1455         if (!isAdmin) {
1456             throw new SecurityException("Only admin users can adopt sd cards");
1457         }
1458     }
1459 
1460     /**
1461      * Constructs a new MountService instance
1462      *
1463      * @param context  Binder context for this service
1464      */
MountService(Context context)1465     public MountService(Context context) {
1466         sSelf = this;
1467 
1468         mContext = context;
1469         mCallbacks = new Callbacks(FgThread.get().getLooper());
1470         mLockPatternUtils = new LockPatternUtils(mContext);
1471 
1472         // XXX: This will go away soon in favor of IMountServiceObserver
1473         mPms = (PackageManagerService) ServiceManager.getService("package");
1474 
1475         HandlerThread hthread = new HandlerThread(TAG);
1476         hthread.start();
1477         mHandler = new MountServiceHandler(hthread.getLooper());
1478 
1479         // Add OBB Action Handler to MountService thread.
1480         mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper());
1481 
1482         // Initialize the last-fstrim tracking if necessary
1483         File dataDir = Environment.getDataDirectory();
1484         File systemDir = new File(dataDir, "system");
1485         mLastMaintenanceFile = new File(systemDir, LAST_FSTRIM_FILE);
1486         if (!mLastMaintenanceFile.exists()) {
1487             // Not setting mLastMaintenance here means that we will force an
1488             // fstrim during reboot following the OTA that installs this code.
1489             try {
1490                 (new FileOutputStream(mLastMaintenanceFile)).close();
1491             } catch (IOException e) {
1492                 Slog.e(TAG, "Unable to create fstrim record " + mLastMaintenanceFile.getPath());
1493             }
1494         } else {
1495             mLastMaintenance = mLastMaintenanceFile.lastModified();
1496         }
1497 
1498         mSettingsFile = new AtomicFile(
1499                 new File(Environment.getDataSystemDirectory(), "storage.xml"));
1500 
1501         synchronized (mLock) {
1502             readSettingsLocked();
1503         }
1504 
1505         LocalServices.addService(MountServiceInternal.class, mMountServiceInternal);
1506 
1507         /*
1508          * Create the connection to vold with a maximum queue of twice the
1509          * amount of containers we'd ever expect to have. This keeps an
1510          * "asec list" from blocking a thread repeatedly.
1511          */
1512 
1513         mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25,
1514                 null);
1515         mConnector.setDebug(true);
1516         mConnector.setWarnIfHeld(mLock);
1517         mConnectorThread = new Thread(mConnector, VOLD_TAG);
1518 
1519         // Reuse parameters from first connector since they are tested and safe
1520         mCryptConnector = new NativeDaemonConnector(this, "cryptd",
1521                 MAX_CONTAINERS * 2, CRYPTD_TAG, 25, null);
1522         mCryptConnector.setDebug(true);
1523         mCryptConnectorThread = new Thread(mCryptConnector, CRYPTD_TAG);
1524 
1525         final IntentFilter userFilter = new IntentFilter();
1526         userFilter.addAction(Intent.ACTION_USER_ADDED);
1527         userFilter.addAction(Intent.ACTION_USER_REMOVED);
1528         mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);
1529 
1530         synchronized (mLock) {
1531             addInternalVolumeLocked();
1532         }
1533 
1534         // Add ourself to the Watchdog monitors if enabled.
1535         if (WATCHDOG_ENABLE) {
1536             Watchdog.getInstance().addMonitor(this);
1537         }
1538     }
1539 
start()1540     private void start() {
1541         mConnectorThread.start();
1542         mCryptConnectorThread.start();
1543     }
1544 
systemReady()1545     private void systemReady() {
1546         mSystemReady = true;
1547         mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget();
1548     }
1549 
bootCompleted()1550     private void bootCompleted() {
1551         mBootCompleted = true;
1552     }
1553 
getDefaultPrimaryStorageUuid()1554     private String getDefaultPrimaryStorageUuid() {
1555         if (SystemProperties.getBoolean(StorageManager.PROP_PRIMARY_PHYSICAL, false)) {
1556             return StorageManager.UUID_PRIMARY_PHYSICAL;
1557         } else {
1558             return StorageManager.UUID_PRIVATE_INTERNAL;
1559         }
1560     }
1561 
readSettingsLocked()1562     private void readSettingsLocked() {
1563         mRecords.clear();
1564         mPrimaryStorageUuid = getDefaultPrimaryStorageUuid();
1565         mForceAdoptable = false;
1566 
1567         FileInputStream fis = null;
1568         try {
1569             fis = mSettingsFile.openRead();
1570             final XmlPullParser in = Xml.newPullParser();
1571             in.setInput(fis, StandardCharsets.UTF_8.name());
1572 
1573             int type;
1574             while ((type = in.next()) != END_DOCUMENT) {
1575                 if (type == START_TAG) {
1576                     final String tag = in.getName();
1577                     if (TAG_VOLUMES.equals(tag)) {
1578                         final int version = readIntAttribute(in, ATTR_VERSION, VERSION_INIT);
1579                         final boolean primaryPhysical = SystemProperties.getBoolean(
1580                                 StorageManager.PROP_PRIMARY_PHYSICAL, false);
1581                         final boolean validAttr = (version >= VERSION_FIX_PRIMARY)
1582                                 || (version >= VERSION_ADD_PRIMARY && !primaryPhysical);
1583                         if (validAttr) {
1584                             mPrimaryStorageUuid = readStringAttribute(in,
1585                                     ATTR_PRIMARY_STORAGE_UUID);
1586                         }
1587                         mForceAdoptable = readBooleanAttribute(in, ATTR_FORCE_ADOPTABLE, false);
1588 
1589                     } else if (TAG_VOLUME.equals(tag)) {
1590                         final VolumeRecord rec = readVolumeRecord(in);
1591                         mRecords.put(rec.fsUuid, rec);
1592                     }
1593                 }
1594             }
1595         } catch (FileNotFoundException e) {
1596             // Missing metadata is okay, probably first boot
1597         } catch (IOException e) {
1598             Slog.wtf(TAG, "Failed reading metadata", e);
1599         } catch (XmlPullParserException e) {
1600             Slog.wtf(TAG, "Failed reading metadata", e);
1601         } finally {
1602             IoUtils.closeQuietly(fis);
1603         }
1604     }
1605 
writeSettingsLocked()1606     private void writeSettingsLocked() {
1607         FileOutputStream fos = null;
1608         try {
1609             fos = mSettingsFile.startWrite();
1610 
1611             XmlSerializer out = new FastXmlSerializer();
1612             out.setOutput(fos, StandardCharsets.UTF_8.name());
1613             out.startDocument(null, true);
1614             out.startTag(null, TAG_VOLUMES);
1615             writeIntAttribute(out, ATTR_VERSION, VERSION_FIX_PRIMARY);
1616             writeStringAttribute(out, ATTR_PRIMARY_STORAGE_UUID, mPrimaryStorageUuid);
1617             writeBooleanAttribute(out, ATTR_FORCE_ADOPTABLE, mForceAdoptable);
1618             final int size = mRecords.size();
1619             for (int i = 0; i < size; i++) {
1620                 final VolumeRecord rec = mRecords.valueAt(i);
1621                 writeVolumeRecord(out, rec);
1622             }
1623             out.endTag(null, TAG_VOLUMES);
1624             out.endDocument();
1625 
1626             mSettingsFile.finishWrite(fos);
1627         } catch (IOException e) {
1628             if (fos != null) {
1629                 mSettingsFile.failWrite(fos);
1630             }
1631         }
1632     }
1633 
readVolumeRecord(XmlPullParser in)1634     public static VolumeRecord readVolumeRecord(XmlPullParser in) throws IOException {
1635         final int type = readIntAttribute(in, ATTR_TYPE);
1636         final String fsUuid = readStringAttribute(in, ATTR_FS_UUID);
1637         final VolumeRecord meta = new VolumeRecord(type, fsUuid);
1638         meta.partGuid = readStringAttribute(in, ATTR_PART_GUID);
1639         meta.nickname = readStringAttribute(in, ATTR_NICKNAME);
1640         meta.userFlags = readIntAttribute(in, ATTR_USER_FLAGS);
1641         meta.createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS);
1642         meta.lastTrimMillis = readLongAttribute(in, ATTR_LAST_TRIM_MILLIS);
1643         meta.lastBenchMillis = readLongAttribute(in, ATTR_LAST_BENCH_MILLIS);
1644         return meta;
1645     }
1646 
writeVolumeRecord(XmlSerializer out, VolumeRecord rec)1647     public static void writeVolumeRecord(XmlSerializer out, VolumeRecord rec) throws IOException {
1648         out.startTag(null, TAG_VOLUME);
1649         writeIntAttribute(out, ATTR_TYPE, rec.type);
1650         writeStringAttribute(out, ATTR_FS_UUID, rec.fsUuid);
1651         writeStringAttribute(out, ATTR_PART_GUID, rec.partGuid);
1652         writeStringAttribute(out, ATTR_NICKNAME, rec.nickname);
1653         writeIntAttribute(out, ATTR_USER_FLAGS, rec.userFlags);
1654         writeLongAttribute(out, ATTR_CREATED_MILLIS, rec.createdMillis);
1655         writeLongAttribute(out, ATTR_LAST_TRIM_MILLIS, rec.lastTrimMillis);
1656         writeLongAttribute(out, ATTR_LAST_BENCH_MILLIS, rec.lastBenchMillis);
1657         out.endTag(null, TAG_VOLUME);
1658     }
1659 
1660     /**
1661      * Exposed API calls below here
1662      */
1663 
1664     @Override
registerListener(IMountServiceListener listener)1665     public void registerListener(IMountServiceListener listener) {
1666         mCallbacks.register(listener);
1667     }
1668 
1669     @Override
unregisterListener(IMountServiceListener listener)1670     public void unregisterListener(IMountServiceListener listener) {
1671         mCallbacks.unregister(listener);
1672     }
1673 
1674     @Override
shutdown(final IMountShutdownObserver observer)1675     public void shutdown(final IMountShutdownObserver observer) {
1676         enforcePermission(android.Manifest.permission.SHUTDOWN);
1677 
1678         Slog.i(TAG, "Shutting down");
1679         mHandler.obtainMessage(H_SHUTDOWN, observer).sendToTarget();
1680     }
1681 
1682     @Override
isUsbMassStorageConnected()1683     public boolean isUsbMassStorageConnected() {
1684         throw new UnsupportedOperationException();
1685     }
1686 
1687     @Override
setUsbMassStorageEnabled(boolean enable)1688     public void setUsbMassStorageEnabled(boolean enable) {
1689         throw new UnsupportedOperationException();
1690     }
1691 
1692     @Override
isUsbMassStorageEnabled()1693     public boolean isUsbMassStorageEnabled() {
1694         throw new UnsupportedOperationException();
1695     }
1696 
1697     @Override
getVolumeState(String mountPoint)1698     public String getVolumeState(String mountPoint) {
1699         throw new UnsupportedOperationException();
1700     }
1701 
1702     @Override
isExternalStorageEmulated()1703     public boolean isExternalStorageEmulated() {
1704         throw new UnsupportedOperationException();
1705     }
1706 
1707     @Override
mountVolume(String path)1708     public int mountVolume(String path) {
1709         mount(findVolumeIdForPathOrThrow(path));
1710         return 0;
1711     }
1712 
1713     @Override
unmountVolume(String path, boolean force, boolean removeEncryption)1714     public void unmountVolume(String path, boolean force, boolean removeEncryption) {
1715         unmount(findVolumeIdForPathOrThrow(path));
1716     }
1717 
1718     @Override
formatVolume(String path)1719     public int formatVolume(String path) {
1720         format(findVolumeIdForPathOrThrow(path));
1721         return 0;
1722     }
1723 
1724     @Override
mount(String volId)1725     public void mount(String volId) {
1726         enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1727         waitForReady();
1728 
1729         final VolumeInfo vol = findVolumeByIdOrThrow(volId);
1730         if (isMountDisallowed(vol)) {
1731             throw new SecurityException("Mounting " + volId + " restricted by policy");
1732         }
1733         try {
1734             mConnector.execute("volume", "mount", vol.id, vol.mountFlags, vol.mountUserId);
1735         } catch (NativeDaemonConnectorException e) {
1736             throw e.rethrowAsParcelableException();
1737         }
1738     }
1739 
1740     @Override
unmount(String volId)1741     public void unmount(String volId) {
1742         enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1743         waitForReady();
1744 
1745         final VolumeInfo vol = findVolumeByIdOrThrow(volId);
1746 
1747         // TODO: expand PMS to know about multiple volumes
1748         if (vol.isPrimaryPhysical()) {
1749             final long ident = Binder.clearCallingIdentity();
1750             try {
1751                 synchronized (mUnmountLock) {
1752                     mUnmountSignal = new CountDownLatch(1);
1753                     mPms.updateExternalMediaStatus(false, true);
1754                     waitForLatch(mUnmountSignal, "mUnmountSignal");
1755                     mUnmountSignal = null;
1756                 }
1757             } finally {
1758                 Binder.restoreCallingIdentity(ident);
1759             }
1760         }
1761 
1762         try {
1763             mConnector.execute("volume", "unmount", vol.id);
1764         } catch (NativeDaemonConnectorException e) {
1765             throw e.rethrowAsParcelableException();
1766         }
1767     }
1768 
1769     @Override
format(String volId)1770     public void format(String volId) {
1771         enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
1772         waitForReady();
1773 
1774         final VolumeInfo vol = findVolumeByIdOrThrow(volId);
1775         try {
1776             mConnector.execute("volume", "format", vol.id, "auto");
1777         } catch (NativeDaemonConnectorException e) {
1778             throw e.rethrowAsParcelableException();
1779         }
1780     }
1781 
1782     @Override
benchmark(String volId)1783     public long benchmark(String volId) {
1784         enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
1785         waitForReady();
1786 
1787         try {
1788             // TODO: make benchmark async so we don't block other commands
1789             final NativeDaemonEvent res = mConnector.execute(3 * DateUtils.MINUTE_IN_MILLIS,
1790                     "volume", "benchmark", volId);
1791             return Long.parseLong(res.getMessage());
1792         } catch (NativeDaemonTimeoutException e) {
1793             return Long.MAX_VALUE;
1794         } catch (NativeDaemonConnectorException e) {
1795             throw e.rethrowAsParcelableException();
1796         }
1797     }
1798 
1799     @Override
partitionPublic(String diskId)1800     public void partitionPublic(String diskId) {
1801         enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
1802         waitForReady();
1803 
1804         final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
1805         try {
1806             mConnector.execute("volume", "partition", diskId, "public");
1807             waitForLatch(latch, "partitionPublic", 3 * DateUtils.MINUTE_IN_MILLIS);
1808         } catch (NativeDaemonConnectorException e) {
1809             throw e.rethrowAsParcelableException();
1810         } catch (TimeoutException e) {
1811             throw new IllegalStateException(e);
1812         }
1813     }
1814 
1815     @Override
partitionPrivate(String diskId)1816     public void partitionPrivate(String diskId) {
1817         enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
1818         enforceAdminUser();
1819         waitForReady();
1820 
1821         final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
1822         try {
1823             mConnector.execute("volume", "partition", diskId, "private");
1824             waitForLatch(latch, "partitionPrivate", 3 * DateUtils.MINUTE_IN_MILLIS);
1825         } catch (NativeDaemonConnectorException e) {
1826             throw e.rethrowAsParcelableException();
1827         } catch (TimeoutException e) {
1828             throw new IllegalStateException(e);
1829         }
1830     }
1831 
1832     @Override
partitionMixed(String diskId, int ratio)1833     public void partitionMixed(String diskId, int ratio) {
1834         enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
1835         enforceAdminUser();
1836         waitForReady();
1837 
1838         final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
1839         try {
1840             mConnector.execute("volume", "partition", diskId, "mixed", ratio);
1841             waitForLatch(latch, "partitionMixed", 3 * DateUtils.MINUTE_IN_MILLIS);
1842         } catch (NativeDaemonConnectorException e) {
1843             throw e.rethrowAsParcelableException();
1844         } catch (TimeoutException e) {
1845             throw new IllegalStateException(e);
1846         }
1847     }
1848 
1849     @Override
setVolumeNickname(String fsUuid, String nickname)1850     public void setVolumeNickname(String fsUuid, String nickname) {
1851         enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1852         waitForReady();
1853 
1854         Preconditions.checkNotNull(fsUuid);
1855         synchronized (mLock) {
1856             final VolumeRecord rec = mRecords.get(fsUuid);
1857             rec.nickname = nickname;
1858             mCallbacks.notifyVolumeRecordChanged(rec);
1859             writeSettingsLocked();
1860         }
1861     }
1862 
1863     @Override
setVolumeUserFlags(String fsUuid, int flags, int mask)1864     public void setVolumeUserFlags(String fsUuid, int flags, int mask) {
1865         enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1866         waitForReady();
1867 
1868         Preconditions.checkNotNull(fsUuid);
1869         synchronized (mLock) {
1870             final VolumeRecord rec = mRecords.get(fsUuid);
1871             rec.userFlags = (rec.userFlags & ~mask) | (flags & mask);
1872             mCallbacks.notifyVolumeRecordChanged(rec);
1873             writeSettingsLocked();
1874         }
1875     }
1876 
1877     @Override
forgetVolume(String fsUuid)1878     public void forgetVolume(String fsUuid) {
1879         enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1880         waitForReady();
1881 
1882         Preconditions.checkNotNull(fsUuid);
1883 
1884         synchronized (mLock) {
1885             final VolumeRecord rec = mRecords.remove(fsUuid);
1886             if (rec != null && !TextUtils.isEmpty(rec.partGuid)) {
1887                 mHandler.obtainMessage(H_PARTITION_FORGET, rec.partGuid).sendToTarget();
1888             }
1889             mCallbacks.notifyVolumeForgotten(fsUuid);
1890 
1891             // If this had been primary storage, revert back to internal and
1892             // reset vold so we bind into new volume into place.
1893             if (Objects.equals(mPrimaryStorageUuid, fsUuid)) {
1894                 mPrimaryStorageUuid = getDefaultPrimaryStorageUuid();
1895                 mHandler.obtainMessage(H_RESET).sendToTarget();
1896             }
1897 
1898             writeSettingsLocked();
1899         }
1900     }
1901 
1902     @Override
forgetAllVolumes()1903     public void forgetAllVolumes() {
1904         enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1905         waitForReady();
1906 
1907         synchronized (mLock) {
1908             for (int i = 0; i < mRecords.size(); i++) {
1909                 final String fsUuid = mRecords.keyAt(i);
1910                 final VolumeRecord rec = mRecords.valueAt(i);
1911                 if (!TextUtils.isEmpty(rec.partGuid)) {
1912                     mHandler.obtainMessage(H_PARTITION_FORGET, rec.partGuid).sendToTarget();
1913                 }
1914                 mCallbacks.notifyVolumeForgotten(fsUuid);
1915             }
1916             mRecords.clear();
1917 
1918             if (!Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, mPrimaryStorageUuid)) {
1919                 mPrimaryStorageUuid = getDefaultPrimaryStorageUuid();
1920             }
1921 
1922             writeSettingsLocked();
1923             mHandler.obtainMessage(H_RESET).sendToTarget();
1924         }
1925     }
1926 
forgetPartition(String partGuid)1927     private void forgetPartition(String partGuid) {
1928         try {
1929             mConnector.execute("volume", "forget_partition", partGuid);
1930         } catch (NativeDaemonConnectorException e) {
1931             Slog.w(TAG, "Failed to forget key for " + partGuid + ": " + e);
1932         }
1933     }
1934 
remountUidExternalStorage(int uid, int mode)1935     private void remountUidExternalStorage(int uid, int mode) {
1936         waitForReady();
1937 
1938         String modeName = "none";
1939         switch (mode) {
1940             case Zygote.MOUNT_EXTERNAL_DEFAULT: {
1941                 modeName = "default";
1942             } break;
1943 
1944             case Zygote.MOUNT_EXTERNAL_READ: {
1945                 modeName = "read";
1946             } break;
1947 
1948             case Zygote.MOUNT_EXTERNAL_WRITE: {
1949                 modeName = "write";
1950             } break;
1951         }
1952 
1953         try {
1954             mConnector.execute("volume", "remount_uid", uid, modeName);
1955         } catch (NativeDaemonConnectorException e) {
1956             Slog.w(TAG, "Failed to remount UID " + uid + " as " + modeName + ": " + e);
1957         }
1958     }
1959 
1960     @Override
setDebugFlags(int flags, int mask)1961     public void setDebugFlags(int flags, int mask) {
1962         enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1963         waitForReady();
1964 
1965         if ((mask & StorageManager.DEBUG_EMULATE_FBE) != 0) {
1966             if (StorageManager.isFileEncryptedNativeOnly()) {
1967                 throw new IllegalStateException(
1968                         "Emulation not available on device with native FBE");
1969             }
1970             if (mLockPatternUtils.isCredentialRequiredToDecrypt(false)) {
1971                 throw new IllegalStateException(
1972                         "Emulation requires disabling 'Secure start-up' in Settings > Security");
1973             }
1974 
1975             final long token = Binder.clearCallingIdentity();
1976             try {
1977                 final boolean emulateFbe = (flags & StorageManager.DEBUG_EMULATE_FBE) != 0;
1978                 SystemProperties.set(StorageManager.PROP_EMULATE_FBE, Boolean.toString(emulateFbe));
1979 
1980                 // Perform hard reboot to kick policy into place
1981                 mContext.getSystemService(PowerManager.class).reboot(null);
1982             } finally {
1983                 Binder.restoreCallingIdentity(token);
1984             }
1985         }
1986 
1987         if ((mask & StorageManager.DEBUG_FORCE_ADOPTABLE) != 0) {
1988             synchronized (mLock) {
1989                 mForceAdoptable = (flags & StorageManager.DEBUG_FORCE_ADOPTABLE) != 0;
1990 
1991                 writeSettingsLocked();
1992                 mHandler.obtainMessage(H_RESET).sendToTarget();
1993             }
1994         }
1995 
1996         if ((mask & (StorageManager.DEBUG_SDCARDFS_FORCE_ON
1997                 | StorageManager.DEBUG_SDCARDFS_FORCE_OFF)) != 0) {
1998             final String value;
1999             if ((flags & StorageManager.DEBUG_SDCARDFS_FORCE_ON) != 0) {
2000                 value = "force_on";
2001             } else if ((flags & StorageManager.DEBUG_SDCARDFS_FORCE_OFF) != 0) {
2002                 value = "force_off";
2003             } else {
2004                 value = "";
2005             }
2006 
2007             final long token = Binder.clearCallingIdentity();
2008             try {
2009                 SystemProperties.set(StorageManager.PROP_SDCARDFS, value);
2010 
2011                 // Reset storage to kick new setting into place
2012                 mHandler.obtainMessage(H_RESET).sendToTarget();
2013             } finally {
2014                 Binder.restoreCallingIdentity(token);
2015             }
2016         }
2017     }
2018 
2019     @Override
getPrimaryStorageUuid()2020     public String getPrimaryStorageUuid() {
2021         enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
2022         waitForReady();
2023 
2024         synchronized (mLock) {
2025             return mPrimaryStorageUuid;
2026         }
2027     }
2028 
2029     @Override
setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback)2030     public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) {
2031         enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
2032         waitForReady();
2033 
2034         final VolumeInfo from;
2035         final VolumeInfo to;
2036 
2037         synchronized (mLock) {
2038             if (Objects.equals(mPrimaryStorageUuid, volumeUuid)) {
2039                 throw new IllegalArgumentException("Primary storage already at " + volumeUuid);
2040             }
2041 
2042             if (mMoveCallback != null) {
2043                 throw new IllegalStateException("Move already in progress");
2044             }
2045             mMoveCallback = callback;
2046             mMoveTargetUuid = volumeUuid;
2047 
2048             // When moving to/from primary physical volume, we probably just nuked
2049             // the current storage location, so we have nothing to move.
2050             if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, mPrimaryStorageUuid)
2051                     || Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
2052                 Slog.d(TAG, "Skipping move to/from primary physical");
2053                 onMoveStatusLocked(MOVE_STATUS_COPY_FINISHED);
2054                 onMoveStatusLocked(PackageManager.MOVE_SUCCEEDED);
2055                 mHandler.obtainMessage(H_RESET).sendToTarget();
2056                 return;
2057 
2058             } else {
2059                 from = findStorageForUuid(mPrimaryStorageUuid);
2060                 to = findStorageForUuid(volumeUuid);
2061 
2062                 if (from == null) {
2063                     Slog.w(TAG, "Failing move due to missing from volume " + mPrimaryStorageUuid);
2064                     onMoveStatusLocked(PackageManager.MOVE_FAILED_INTERNAL_ERROR);
2065                     return;
2066                 } else if (to == null) {
2067                     Slog.w(TAG, "Failing move due to missing to volume " + volumeUuid);
2068                     onMoveStatusLocked(PackageManager.MOVE_FAILED_INTERNAL_ERROR);
2069                     return;
2070                 }
2071             }
2072         }
2073 
2074         try {
2075             mConnector.execute("volume", "move_storage", from.id, to.id);
2076         } catch (NativeDaemonConnectorException e) {
2077             throw e.rethrowAsParcelableException();
2078         }
2079     }
2080 
2081     @Override
getStorageUsers(String path)2082     public int[] getStorageUsers(String path) {
2083         enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
2084         waitForReady();
2085         try {
2086             final String[] r = NativeDaemonEvent.filterMessageList(
2087                     mConnector.executeForList("storage", "users", path),
2088                     VoldResponseCode.StorageUsersListResult);
2089 
2090             // FMT: <pid> <process name>
2091             int[] data = new int[r.length];
2092             for (int i = 0; i < r.length; i++) {
2093                 String[] tok = r[i].split(" ");
2094                 try {
2095                     data[i] = Integer.parseInt(tok[0]);
2096                 } catch (NumberFormatException nfe) {
2097                     Slog.e(TAG, String.format("Error parsing pid %s", tok[0]));
2098                     return new int[0];
2099                 }
2100             }
2101             return data;
2102         } catch (NativeDaemonConnectorException e) {
2103             Slog.e(TAG, "Failed to retrieve storage users list", e);
2104             return new int[0];
2105         }
2106     }
2107 
warnOnNotMounted()2108     private void warnOnNotMounted() {
2109         synchronized (mLock) {
2110             for (int i = 0; i < mVolumes.size(); i++) {
2111                 final VolumeInfo vol = mVolumes.valueAt(i);
2112                 if (vol.isPrimary() && vol.isMountedWritable()) {
2113                     // Cool beans, we have a mounted primary volume
2114                     return;
2115                 }
2116             }
2117         }
2118 
2119         Slog.w(TAG, "No primary storage mounted!");
2120     }
2121 
getSecureContainerList()2122     public String[] getSecureContainerList() {
2123         enforcePermission(android.Manifest.permission.ASEC_ACCESS);
2124         waitForReady();
2125         warnOnNotMounted();
2126 
2127         try {
2128             return NativeDaemonEvent.filterMessageList(
2129                     mConnector.executeForList("asec", "list"), VoldResponseCode.AsecListResult);
2130         } catch (NativeDaemonConnectorException e) {
2131             return new String[0];
2132         }
2133     }
2134 
createSecureContainer(String id, int sizeMb, String fstype, String key, int ownerUid, boolean external)2135     public int createSecureContainer(String id, int sizeMb, String fstype, String key,
2136             int ownerUid, boolean external) {
2137         enforcePermission(android.Manifest.permission.ASEC_CREATE);
2138         waitForReady();
2139         warnOnNotMounted();
2140 
2141         int rc = StorageResultCode.OperationSucceeded;
2142         try {
2143             mConnector.execute("asec", "create", id, sizeMb, fstype, new SensitiveArg(key),
2144                     ownerUid, external ? "1" : "0");
2145         } catch (NativeDaemonConnectorException e) {
2146             rc = StorageResultCode.OperationFailedInternalError;
2147         }
2148 
2149         if (rc == StorageResultCode.OperationSucceeded) {
2150             synchronized (mAsecMountSet) {
2151                 mAsecMountSet.add(id);
2152             }
2153         }
2154         return rc;
2155     }
2156 
2157     @Override
resizeSecureContainer(String id, int sizeMb, String key)2158     public int resizeSecureContainer(String id, int sizeMb, String key) {
2159         enforcePermission(android.Manifest.permission.ASEC_CREATE);
2160         waitForReady();
2161         warnOnNotMounted();
2162 
2163         int rc = StorageResultCode.OperationSucceeded;
2164         try {
2165             mConnector.execute("asec", "resize", id, sizeMb, new SensitiveArg(key));
2166         } catch (NativeDaemonConnectorException e) {
2167             rc = StorageResultCode.OperationFailedInternalError;
2168         }
2169         return rc;
2170     }
2171 
finalizeSecureContainer(String id)2172     public int finalizeSecureContainer(String id) {
2173         enforcePermission(android.Manifest.permission.ASEC_CREATE);
2174         warnOnNotMounted();
2175 
2176         int rc = StorageResultCode.OperationSucceeded;
2177         try {
2178             mConnector.execute("asec", "finalize", id);
2179             /*
2180              * Finalization does a remount, so no need
2181              * to update mAsecMountSet
2182              */
2183         } catch (NativeDaemonConnectorException e) {
2184             rc = StorageResultCode.OperationFailedInternalError;
2185         }
2186         return rc;
2187     }
2188 
fixPermissionsSecureContainer(String id, int gid, String filename)2189     public int fixPermissionsSecureContainer(String id, int gid, String filename) {
2190         enforcePermission(android.Manifest.permission.ASEC_CREATE);
2191         warnOnNotMounted();
2192 
2193         int rc = StorageResultCode.OperationSucceeded;
2194         try {
2195             mConnector.execute("asec", "fixperms", id, gid, filename);
2196             /*
2197              * Fix permissions does a remount, so no need to update
2198              * mAsecMountSet
2199              */
2200         } catch (NativeDaemonConnectorException e) {
2201             rc = StorageResultCode.OperationFailedInternalError;
2202         }
2203         return rc;
2204     }
2205 
destroySecureContainer(String id, boolean force)2206     public int destroySecureContainer(String id, boolean force) {
2207         enforcePermission(android.Manifest.permission.ASEC_DESTROY);
2208         waitForReady();
2209         warnOnNotMounted();
2210 
2211         /*
2212          * Force a GC to make sure AssetManagers in other threads of the
2213          * system_server are cleaned up. We have to do this since AssetManager
2214          * instances are kept as a WeakReference and it's possible we have files
2215          * open on the external storage.
2216          */
2217         Runtime.getRuntime().gc();
2218 
2219         int rc = StorageResultCode.OperationSucceeded;
2220         try {
2221             final Command cmd = new Command("asec", "destroy", id);
2222             if (force) {
2223                 cmd.appendArg("force");
2224             }
2225             mConnector.execute(cmd);
2226         } catch (NativeDaemonConnectorException e) {
2227             int code = e.getCode();
2228             if (code == VoldResponseCode.OpFailedStorageBusy) {
2229                 rc = StorageResultCode.OperationFailedStorageBusy;
2230             } else {
2231                 rc = StorageResultCode.OperationFailedInternalError;
2232             }
2233         }
2234 
2235         if (rc == StorageResultCode.OperationSucceeded) {
2236             synchronized (mAsecMountSet) {
2237                 if (mAsecMountSet.contains(id)) {
2238                     mAsecMountSet.remove(id);
2239                 }
2240             }
2241         }
2242 
2243         return rc;
2244     }
2245 
mountSecureContainer(String id, String key, int ownerUid, boolean readOnly)2246     public int mountSecureContainer(String id, String key, int ownerUid, boolean readOnly) {
2247         enforcePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
2248         waitForReady();
2249         warnOnNotMounted();
2250 
2251         synchronized (mAsecMountSet) {
2252             if (mAsecMountSet.contains(id)) {
2253                 return StorageResultCode.OperationFailedStorageMounted;
2254             }
2255         }
2256 
2257         int rc = StorageResultCode.OperationSucceeded;
2258         try {
2259             mConnector.execute("asec", "mount", id, new SensitiveArg(key), ownerUid,
2260                     readOnly ? "ro" : "rw");
2261         } catch (NativeDaemonConnectorException e) {
2262             int code = e.getCode();
2263             if (code != VoldResponseCode.OpFailedStorageBusy) {
2264                 rc = StorageResultCode.OperationFailedInternalError;
2265             }
2266         }
2267 
2268         if (rc == StorageResultCode.OperationSucceeded) {
2269             synchronized (mAsecMountSet) {
2270                 mAsecMountSet.add(id);
2271             }
2272         }
2273         return rc;
2274     }
2275 
unmountSecureContainer(String id, boolean force)2276     public int unmountSecureContainer(String id, boolean force) {
2277         enforcePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
2278         waitForReady();
2279         warnOnNotMounted();
2280 
2281         synchronized (mAsecMountSet) {
2282             if (!mAsecMountSet.contains(id)) {
2283                 return StorageResultCode.OperationFailedStorageNotMounted;
2284             }
2285          }
2286 
2287         /*
2288          * Force a GC to make sure AssetManagers in other threads of the
2289          * system_server are cleaned up. We have to do this since AssetManager
2290          * instances are kept as a WeakReference and it's possible we have files
2291          * open on the external storage.
2292          */
2293         Runtime.getRuntime().gc();
2294 
2295         int rc = StorageResultCode.OperationSucceeded;
2296         try {
2297             final Command cmd = new Command("asec", "unmount", id);
2298             if (force) {
2299                 cmd.appendArg("force");
2300             }
2301             mConnector.execute(cmd);
2302         } catch (NativeDaemonConnectorException e) {
2303             int code = e.getCode();
2304             if (code == VoldResponseCode.OpFailedStorageBusy) {
2305                 rc = StorageResultCode.OperationFailedStorageBusy;
2306             } else {
2307                 rc = StorageResultCode.OperationFailedInternalError;
2308             }
2309         }
2310 
2311         if (rc == StorageResultCode.OperationSucceeded) {
2312             synchronized (mAsecMountSet) {
2313                 mAsecMountSet.remove(id);
2314             }
2315         }
2316         return rc;
2317     }
2318 
isSecureContainerMounted(String id)2319     public boolean isSecureContainerMounted(String id) {
2320         enforcePermission(android.Manifest.permission.ASEC_ACCESS);
2321         waitForReady();
2322         warnOnNotMounted();
2323 
2324         synchronized (mAsecMountSet) {
2325             return mAsecMountSet.contains(id);
2326         }
2327     }
2328 
renameSecureContainer(String oldId, String newId)2329     public int renameSecureContainer(String oldId, String newId) {
2330         enforcePermission(android.Manifest.permission.ASEC_RENAME);
2331         waitForReady();
2332         warnOnNotMounted();
2333 
2334         synchronized (mAsecMountSet) {
2335             /*
2336              * Because a mounted container has active internal state which cannot be
2337              * changed while active, we must ensure both ids are not currently mounted.
2338              */
2339             if (mAsecMountSet.contains(oldId) || mAsecMountSet.contains(newId)) {
2340                 return StorageResultCode.OperationFailedStorageMounted;
2341             }
2342         }
2343 
2344         int rc = StorageResultCode.OperationSucceeded;
2345         try {
2346             mConnector.execute("asec", "rename", oldId, newId);
2347         } catch (NativeDaemonConnectorException e) {
2348             rc = StorageResultCode.OperationFailedInternalError;
2349         }
2350 
2351         return rc;
2352     }
2353 
getSecureContainerPath(String id)2354     public String getSecureContainerPath(String id) {
2355         enforcePermission(android.Manifest.permission.ASEC_ACCESS);
2356         waitForReady();
2357         warnOnNotMounted();
2358 
2359         final NativeDaemonEvent event;
2360         try {
2361             event = mConnector.execute("asec", "path", id);
2362             event.checkCode(VoldResponseCode.AsecPathResult);
2363             return event.getMessage();
2364         } catch (NativeDaemonConnectorException e) {
2365             int code = e.getCode();
2366             if (code == VoldResponseCode.OpFailedStorageNotFound) {
2367                 Slog.i(TAG, String.format("Container '%s' not found", id));
2368                 return null;
2369             } else {
2370                 throw new IllegalStateException(String.format("Unexpected response code %d", code));
2371             }
2372         }
2373     }
2374 
getSecureContainerFilesystemPath(String id)2375     public String getSecureContainerFilesystemPath(String id) {
2376         enforcePermission(android.Manifest.permission.ASEC_ACCESS);
2377         waitForReady();
2378         warnOnNotMounted();
2379 
2380         final NativeDaemonEvent event;
2381         try {
2382             event = mConnector.execute("asec", "fspath", id);
2383             event.checkCode(VoldResponseCode.AsecPathResult);
2384             return event.getMessage();
2385         } catch (NativeDaemonConnectorException e) {
2386             int code = e.getCode();
2387             if (code == VoldResponseCode.OpFailedStorageNotFound) {
2388                 Slog.i(TAG, String.format("Container '%s' not found", id));
2389                 return null;
2390             } else {
2391                 throw new IllegalStateException(String.format("Unexpected response code %d", code));
2392             }
2393         }
2394     }
2395 
2396     @Override
finishMediaUpdate()2397     public void finishMediaUpdate() {
2398         if (Binder.getCallingUid() != Process.SYSTEM_UID) {
2399             throw new SecurityException("no permission to call finishMediaUpdate()");
2400         }
2401         if (mUnmountSignal != null) {
2402             mUnmountSignal.countDown();
2403         } else {
2404             Slog.w(TAG, "Odd, nobody asked to unmount?");
2405         }
2406     }
2407 
isUidOwnerOfPackageOrSystem(String packageName, int callerUid)2408     private boolean isUidOwnerOfPackageOrSystem(String packageName, int callerUid) {
2409         if (callerUid == android.os.Process.SYSTEM_UID) {
2410             return true;
2411         }
2412 
2413         if (packageName == null) {
2414             return false;
2415         }
2416 
2417         final int packageUid = mPms.getPackageUid(packageName,
2418                 PackageManager.MATCH_DEBUG_TRIAGED_MISSING, UserHandle.getUserId(callerUid));
2419 
2420         if (DEBUG_OBB) {
2421             Slog.d(TAG, "packageName = " + packageName + ", packageUid = " +
2422                     packageUid + ", callerUid = " + callerUid);
2423         }
2424 
2425         return callerUid == packageUid;
2426     }
2427 
getMountedObbPath(String rawPath)2428     public String getMountedObbPath(String rawPath) {
2429         Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
2430 
2431         waitForReady();
2432         warnOnNotMounted();
2433 
2434         final ObbState state;
2435         synchronized (mObbMounts) {
2436             state = mObbPathToStateMap.get(rawPath);
2437         }
2438         if (state == null) {
2439             Slog.w(TAG, "Failed to find OBB mounted at " + rawPath);
2440             return null;
2441         }
2442 
2443         final NativeDaemonEvent event;
2444         try {
2445             event = mConnector.execute("obb", "path", state.canonicalPath);
2446             event.checkCode(VoldResponseCode.AsecPathResult);
2447             return event.getMessage();
2448         } catch (NativeDaemonConnectorException e) {
2449             int code = e.getCode();
2450             if (code == VoldResponseCode.OpFailedStorageNotFound) {
2451                 return null;
2452             } else {
2453                 throw new IllegalStateException(String.format("Unexpected response code %d", code));
2454             }
2455         }
2456     }
2457 
2458     @Override
isObbMounted(String rawPath)2459     public boolean isObbMounted(String rawPath) {
2460         Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
2461         synchronized (mObbMounts) {
2462             return mObbPathToStateMap.containsKey(rawPath);
2463         }
2464     }
2465 
2466     @Override
mountObb( String rawPath, String canonicalPath, String key, IObbActionListener token, int nonce)2467     public void mountObb(
2468             String rawPath, String canonicalPath, String key, IObbActionListener token, int nonce) {
2469         Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
2470         Preconditions.checkNotNull(canonicalPath, "canonicalPath cannot be null");
2471         Preconditions.checkNotNull(token, "token cannot be null");
2472 
2473         final int callingUid = Binder.getCallingUid();
2474         final ObbState obbState = new ObbState(rawPath, canonicalPath, callingUid, token, nonce);
2475         final ObbAction action = new MountObbAction(obbState, key, callingUid);
2476         mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
2477 
2478         if (DEBUG_OBB)
2479             Slog.i(TAG, "Send to OBB handler: " + action.toString());
2480     }
2481 
2482     @Override
unmountObb(String rawPath, boolean force, IObbActionListener token, int nonce)2483     public void unmountObb(String rawPath, boolean force, IObbActionListener token, int nonce) {
2484         Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
2485 
2486         final ObbState existingState;
2487         synchronized (mObbMounts) {
2488             existingState = mObbPathToStateMap.get(rawPath);
2489         }
2490 
2491         if (existingState != null) {
2492             // TODO: separate state object from request data
2493             final int callingUid = Binder.getCallingUid();
2494             final ObbState newState = new ObbState(
2495                     rawPath, existingState.canonicalPath, callingUid, token, nonce);
2496             final ObbAction action = new UnmountObbAction(newState, force);
2497             mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
2498 
2499             if (DEBUG_OBB)
2500                 Slog.i(TAG, "Send to OBB handler: " + action.toString());
2501         } else {
2502             Slog.w(TAG, "Unknown OBB mount at " + rawPath);
2503         }
2504     }
2505 
2506     @Override
getEncryptionState()2507     public int getEncryptionState() {
2508         mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2509                 "no permission to access the crypt keeper");
2510 
2511         waitForReady();
2512 
2513         final NativeDaemonEvent event;
2514         try {
2515             event = mCryptConnector.execute("cryptfs", "cryptocomplete");
2516             return Integer.parseInt(event.getMessage());
2517         } catch (NumberFormatException e) {
2518             // Bad result - unexpected.
2519             Slog.w(TAG, "Unable to parse result from cryptfs cryptocomplete");
2520             return ENCRYPTION_STATE_ERROR_UNKNOWN;
2521         } catch (NativeDaemonConnectorException e) {
2522             // Something bad happened.
2523             Slog.w(TAG, "Error in communicating with cryptfs in validating");
2524             return ENCRYPTION_STATE_ERROR_UNKNOWN;
2525         }
2526     }
2527 
2528     @Override
decryptStorage(String password)2529     public int decryptStorage(String password) {
2530         if (TextUtils.isEmpty(password)) {
2531             throw new IllegalArgumentException("password cannot be empty");
2532         }
2533 
2534         mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2535                 "no permission to access the crypt keeper");
2536 
2537         waitForReady();
2538 
2539         if (DEBUG_EVENTS) {
2540             Slog.i(TAG, "decrypting storage...");
2541         }
2542 
2543         final NativeDaemonEvent event;
2544         try {
2545             event = mCryptConnector.execute("cryptfs", "checkpw", new SensitiveArg(password));
2546 
2547             final int code = Integer.parseInt(event.getMessage());
2548             if (code == 0) {
2549                 // Decrypt was successful. Post a delayed message before restarting in order
2550                 // to let the UI to clear itself
2551                 mHandler.postDelayed(new Runnable() {
2552                     public void run() {
2553                         try {
2554                             mCryptConnector.execute("cryptfs", "restart");
2555                         } catch (NativeDaemonConnectorException e) {
2556                             Slog.e(TAG, "problem executing in background", e);
2557                         }
2558                     }
2559                 }, 1000); // 1 second
2560             }
2561 
2562             return code;
2563         } catch (NativeDaemonConnectorException e) {
2564             // Decryption failed
2565             return e.getCode();
2566         }
2567     }
2568 
encryptStorage(int type, String password)2569     public int encryptStorage(int type, String password) {
2570         if (TextUtils.isEmpty(password) && type != StorageManager.CRYPT_TYPE_DEFAULT) {
2571             throw new IllegalArgumentException("password cannot be empty");
2572         }
2573 
2574         mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2575             "no permission to access the crypt keeper");
2576 
2577         waitForReady();
2578 
2579         if (DEBUG_EVENTS) {
2580             Slog.i(TAG, "encrypting storage...");
2581         }
2582 
2583         try {
2584             if (type == StorageManager.CRYPT_TYPE_DEFAULT) {
2585                 mCryptConnector.execute("cryptfs", "enablecrypto", "inplace",
2586                                 CRYPTO_TYPES[type]);
2587             } else {
2588                 mCryptConnector.execute("cryptfs", "enablecrypto", "inplace",
2589                                 CRYPTO_TYPES[type], new SensitiveArg(password));
2590             }
2591         } catch (NativeDaemonConnectorException e) {
2592             // Encryption failed
2593             return e.getCode();
2594         }
2595 
2596         return 0;
2597     }
2598 
2599     /** Set the password for encrypting the master key.
2600      *  @param type One of the CRYPTO_TYPE_XXX consts defined in StorageManager.
2601      *  @param password The password to set.
2602      */
changeEncryptionPassword(int type, String password)2603     public int changeEncryptionPassword(int type, String password) {
2604         mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2605             "no permission to access the crypt keeper");
2606 
2607         waitForReady();
2608 
2609         if (DEBUG_EVENTS) {
2610             Slog.i(TAG, "changing encryption password...");
2611         }
2612 
2613         try {
2614             NativeDaemonEvent event = mCryptConnector.execute("cryptfs", "changepw", CRYPTO_TYPES[type],
2615                         new SensitiveArg(password));
2616             return Integer.parseInt(event.getMessage());
2617         } catch (NativeDaemonConnectorException e) {
2618             // Encryption failed
2619             return e.getCode();
2620         }
2621     }
2622 
2623     /**
2624      * Validate a user-supplied password string with cryptfs
2625      */
2626     @Override
verifyEncryptionPassword(String password)2627     public int verifyEncryptionPassword(String password) throws RemoteException {
2628         // Only the system process is permitted to validate passwords
2629         if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
2630             throw new SecurityException("no permission to access the crypt keeper");
2631         }
2632 
2633         mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2634             "no permission to access the crypt keeper");
2635 
2636         if (TextUtils.isEmpty(password)) {
2637             throw new IllegalArgumentException("password cannot be empty");
2638         }
2639 
2640         waitForReady();
2641 
2642         if (DEBUG_EVENTS) {
2643             Slog.i(TAG, "validating encryption password...");
2644         }
2645 
2646         final NativeDaemonEvent event;
2647         try {
2648             event = mCryptConnector.execute("cryptfs", "verifypw", new SensitiveArg(password));
2649             Slog.i(TAG, "cryptfs verifypw => " + event.getMessage());
2650             return Integer.parseInt(event.getMessage());
2651         } catch (NativeDaemonConnectorException e) {
2652             // Encryption failed
2653             return e.getCode();
2654         }
2655     }
2656 
2657     /**
2658      * Get the type of encryption used to encrypt the master key.
2659      * @return The type, one of the CRYPT_TYPE_XXX consts from StorageManager.
2660      */
2661     @Override
getPasswordType()2662     public int getPasswordType() {
2663         mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL,
2664             "no permission to access the crypt keeper");
2665 
2666         waitForReady();
2667 
2668         final NativeDaemonEvent event;
2669         try {
2670             event = mCryptConnector.execute("cryptfs", "getpwtype");
2671             for (int i = 0; i < CRYPTO_TYPES.length; ++i) {
2672                 if (CRYPTO_TYPES[i].equals(event.getMessage()))
2673                     return i;
2674             }
2675 
2676             throw new IllegalStateException("unexpected return from cryptfs");
2677         } catch (NativeDaemonConnectorException e) {
2678             throw e.rethrowAsParcelableException();
2679         }
2680     }
2681 
2682     /**
2683      * Set a field in the crypto header.
2684      * @param field field to set
2685      * @param contents contents to set in field
2686      */
2687     @Override
setField(String field, String contents)2688     public void setField(String field, String contents) throws RemoteException {
2689         mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL,
2690             "no permission to access the crypt keeper");
2691 
2692         waitForReady();
2693 
2694         final NativeDaemonEvent event;
2695         try {
2696             event = mCryptConnector.execute("cryptfs", "setfield", field, contents);
2697         } catch (NativeDaemonConnectorException e) {
2698             throw e.rethrowAsParcelableException();
2699         }
2700     }
2701 
2702     /**
2703      * Gets a field from the crypto header.
2704      * @param field field to get
2705      * @return contents of field
2706      */
2707     @Override
getField(String field)2708     public String getField(String field) throws RemoteException {
2709         mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL,
2710             "no permission to access the crypt keeper");
2711 
2712         waitForReady();
2713 
2714         final NativeDaemonEvent event;
2715         try {
2716             final String[] contents = NativeDaemonEvent.filterMessageList(
2717                     mCryptConnector.executeForList("cryptfs", "getfield", field),
2718                     VoldResponseCode.CryptfsGetfieldResult);
2719             String result = new String();
2720             for (String content : contents) {
2721                 result += content;
2722             }
2723             return result;
2724         } catch (NativeDaemonConnectorException e) {
2725             throw e.rethrowAsParcelableException();
2726         }
2727     }
2728 
2729     /**
2730      * Is userdata convertible to file based encryption?
2731      * @return non zero for convertible
2732      */
2733     @Override
isConvertibleToFBE()2734     public boolean isConvertibleToFBE() throws RemoteException {
2735         mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL,
2736             "no permission to access the crypt keeper");
2737 
2738         waitForReady();
2739 
2740         final NativeDaemonEvent event;
2741         try {
2742             event = mCryptConnector.execute("cryptfs", "isConvertibleToFBE");
2743             return Integer.parseInt(event.getMessage()) != 0;
2744         } catch (NativeDaemonConnectorException e) {
2745             throw e.rethrowAsParcelableException();
2746         }
2747     }
2748 
2749     @Override
getPassword()2750     public String getPassword() throws RemoteException {
2751         mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL,
2752                 "only keyguard can retrieve password");
2753 
2754         if (!isReady()) {
2755             return new String();
2756         }
2757 
2758         final NativeDaemonEvent event;
2759         try {
2760             event = mCryptConnector.execute("cryptfs", "getpw");
2761             if ("-1".equals(event.getMessage())) {
2762                 // -1 equals no password
2763                 return null;
2764             }
2765             return event.getMessage();
2766         } catch (NativeDaemonConnectorException e) {
2767             throw e.rethrowAsParcelableException();
2768         } catch (IllegalArgumentException e) {
2769             Slog.e(TAG, "Invalid response to getPassword");
2770             return null;
2771         }
2772     }
2773 
2774     @Override
clearPassword()2775     public void clearPassword() throws RemoteException {
2776         mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL,
2777                 "only keyguard can clear password");
2778 
2779         if (!isReady()) {
2780             return;
2781         }
2782 
2783         final NativeDaemonEvent event;
2784         try {
2785             event = mCryptConnector.execute("cryptfs", "clearpw");
2786         } catch (NativeDaemonConnectorException e) {
2787             throw e.rethrowAsParcelableException();
2788         }
2789     }
2790 
2791     @Override
createUserKey(int userId, int serialNumber, boolean ephemeral)2792     public void createUserKey(int userId, int serialNumber, boolean ephemeral) {
2793         enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
2794         waitForReady();
2795 
2796         try {
2797             mCryptConnector.execute("cryptfs", "create_user_key", userId, serialNumber,
2798                 ephemeral ? 1 : 0);
2799         } catch (NativeDaemonConnectorException e) {
2800             throw e.rethrowAsParcelableException();
2801         }
2802     }
2803 
2804     @Override
destroyUserKey(int userId)2805     public void destroyUserKey(int userId) {
2806         enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
2807         waitForReady();
2808 
2809         try {
2810             mCryptConnector.execute("cryptfs", "destroy_user_key", userId);
2811         } catch (NativeDaemonConnectorException e) {
2812             throw e.rethrowAsParcelableException();
2813         }
2814     }
2815 
encodeBytes(byte[] bytes)2816     private SensitiveArg encodeBytes(byte[] bytes) {
2817         if (ArrayUtils.isEmpty(bytes)) {
2818             return new SensitiveArg("!");
2819         } else {
2820             return new SensitiveArg(HexDump.toHexString(bytes));
2821         }
2822     }
2823 
2824     /*
2825      * Add this token/secret pair to the set of ways we can recover a disk encryption key.
2826      * Changing the token/secret for a disk encryption key is done in two phases: first, adding
2827      * a new token/secret pair with this call, then delting all other pairs with
2828      * fixateNewestUserKeyAuth. This allows other places where a credential is used, such as
2829      * Gatekeeper, to be updated between the two calls.
2830      */
2831     @Override
addUserKeyAuth(int userId, int serialNumber, byte[] token, byte[] secret)2832     public void addUserKeyAuth(int userId, int serialNumber, byte[] token, byte[] secret) {
2833         enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
2834         waitForReady();
2835 
2836         try {
2837             mCryptConnector.execute("cryptfs", "add_user_key_auth", userId, serialNumber,
2838                 encodeBytes(token), encodeBytes(secret));
2839         } catch (NativeDaemonConnectorException e) {
2840             throw e.rethrowAsParcelableException();
2841         }
2842     }
2843 
2844     /*
2845      * Delete all disk encryption token/secret pairs except the most recently added one
2846      */
2847     @Override
fixateNewestUserKeyAuth(int userId)2848     public void fixateNewestUserKeyAuth(int userId) {
2849         enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
2850         waitForReady();
2851 
2852         try {
2853             mCryptConnector.execute("cryptfs", "fixate_newest_user_key_auth", userId);
2854         } catch (NativeDaemonConnectorException e) {
2855             throw e.rethrowAsParcelableException();
2856         }
2857     }
2858 
2859     @Override
unlockUserKey(int userId, int serialNumber, byte[] token, byte[] secret)2860     public void unlockUserKey(int userId, int serialNumber, byte[] token, byte[] secret) {
2861         enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
2862         waitForReady();
2863 
2864         if (StorageManager.isFileEncryptedNativeOrEmulated()) {
2865             // When a user has secure lock screen, require a challenge token to
2866             // actually unlock. This check is mostly in place for emulation mode.
2867             if (mLockPatternUtils.isSecure(userId) && ArrayUtils.isEmpty(token)) {
2868                 throw new IllegalStateException("Token required to unlock secure user " + userId);
2869             }
2870 
2871             try {
2872                 mCryptConnector.execute("cryptfs", "unlock_user_key", userId, serialNumber,
2873                         encodeBytes(token), encodeBytes(secret));
2874             } catch (NativeDaemonConnectorException e) {
2875                 throw e.rethrowAsParcelableException();
2876             }
2877         }
2878 
2879         synchronized (mLock) {
2880             mLocalUnlockedUsers = ArrayUtils.appendInt(mLocalUnlockedUsers, userId);
2881         }
2882     }
2883 
2884     @Override
lockUserKey(int userId)2885     public void lockUserKey(int userId) {
2886         enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
2887         waitForReady();
2888 
2889         try {
2890             mCryptConnector.execute("cryptfs", "lock_user_key", userId);
2891         } catch (NativeDaemonConnectorException e) {
2892             throw e.rethrowAsParcelableException();
2893         }
2894 
2895         synchronized (mLock) {
2896             mLocalUnlockedUsers = ArrayUtils.removeInt(mLocalUnlockedUsers, userId);
2897         }
2898     }
2899 
2900     @Override
isUserKeyUnlocked(int userId)2901     public boolean isUserKeyUnlocked(int userId) {
2902         synchronized (mLock) {
2903             return ArrayUtils.contains(mLocalUnlockedUsers, userId);
2904         }
2905     }
2906 
2907     @Override
prepareUserStorage(String volumeUuid, int userId, int serialNumber, int flags)2908     public void prepareUserStorage(String volumeUuid, int userId, int serialNumber, int flags) {
2909         enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
2910         waitForReady();
2911 
2912         try {
2913             mCryptConnector.execute("cryptfs", "prepare_user_storage", escapeNull(volumeUuid),
2914                     userId, serialNumber, flags);
2915         } catch (NativeDaemonConnectorException e) {
2916             throw e.rethrowAsParcelableException();
2917         }
2918     }
2919 
2920     @Override
destroyUserStorage(String volumeUuid, int userId, int flags)2921     public void destroyUserStorage(String volumeUuid, int userId, int flags) {
2922         enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
2923         waitForReady();
2924 
2925         try {
2926             mCryptConnector.execute("cryptfs", "destroy_user_storage", escapeNull(volumeUuid),
2927                     userId, flags);
2928         } catch (NativeDaemonConnectorException e) {
2929             throw e.rethrowAsParcelableException();
2930         }
2931     }
2932 
2933     @Override
mountAppFuse(final String name)2934     public ParcelFileDescriptor mountAppFuse(final String name) throws RemoteException {
2935         try {
2936             final int uid = Binder.getCallingUid();
2937             final int pid = Binder.getCallingPid();
2938             final NativeDaemonEvent event =
2939                     mConnector.execute("appfuse", "mount", uid, pid, name);
2940             if (event.getFileDescriptors() == null) {
2941                 throw new RemoteException("AppFuse FD from vold is null.");
2942             }
2943             return ParcelFileDescriptor.fromFd(
2944                     event.getFileDescriptors()[0],
2945                     mHandler,
2946                     new ParcelFileDescriptor.OnCloseListener() {
2947                         @Override
2948                         public void onClose(IOException e) {
2949                             try {
2950                                 final NativeDaemonEvent event = mConnector.execute(
2951                                         "appfuse", "unmount", uid, pid, name);
2952                             } catch (NativeDaemonConnectorException unmountException) {
2953                                 Log.e(TAG, "Failed to unmount appfuse.");
2954                             }
2955                         }
2956                     });
2957         } catch (NativeDaemonConnectorException e) {
2958             throw e.rethrowAsParcelableException();
2959         } catch (IOException e) {
2960             throw new RemoteException(e.getMessage());
2961         }
2962     }
2963 
2964     @Override
2965     public int mkdirs(String callingPkg, String appPath) {
2966         final int userId = UserHandle.getUserId(Binder.getCallingUid());
2967         final UserEnvironment userEnv = new UserEnvironment(userId);
2968 
2969         // Validate that reported package name belongs to caller
2970         final AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(
2971                 Context.APP_OPS_SERVICE);
2972         appOps.checkPackage(Binder.getCallingUid(), callingPkg);
2973 
2974         File appFile = null;
2975         try {
2976             appFile = new File(appPath).getCanonicalFile();
2977         } catch (IOException e) {
2978             Slog.e(TAG, "Failed to resolve " + appPath + ": " + e);
2979             return -1;
2980         }
2981 
2982         // Try translating the app path into a vold path, but require that it
2983         // belong to the calling package.
2984         if (FileUtils.contains(userEnv.buildExternalStorageAppDataDirs(callingPkg), appFile) ||
2985                 FileUtils.contains(userEnv.buildExternalStorageAppObbDirs(callingPkg), appFile) ||
2986                 FileUtils.contains(userEnv.buildExternalStorageAppMediaDirs(callingPkg), appFile)) {
2987             appPath = appFile.getAbsolutePath();
2988             if (!appPath.endsWith("/")) {
2989                 appPath = appPath + "/";
2990             }
2991 
2992             try {
2993                 mConnector.execute("volume", "mkdirs", appPath);
2994                 return 0;
2995             } catch (NativeDaemonConnectorException e) {
2996                 return e.getCode();
2997             }
2998         }
2999 
3000         throw new SecurityException("Invalid mkdirs path: " + appFile);
3001     }
3002 
3003     @Override
3004     public StorageVolume[] getVolumeList(int uid, String packageName, int flags) {
3005         final int userId = UserHandle.getUserId(uid);
3006 
3007         final boolean forWrite = (flags & StorageManager.FLAG_FOR_WRITE) != 0;
3008         final boolean realState = (flags & StorageManager.FLAG_REAL_STATE) != 0;
3009         final boolean includeInvisible = (flags & StorageManager.FLAG_INCLUDE_INVISIBLE) != 0;
3010 
3011         final boolean userKeyUnlocked;
3012         final boolean storagePermission;
3013         final long token = Binder.clearCallingIdentity();
3014         try {
3015             userKeyUnlocked = isUserKeyUnlocked(userId);
3016             storagePermission = mMountServiceInternal.hasExternalStorage(uid, packageName);
3017         } finally {
3018             Binder.restoreCallingIdentity(token);
3019         }
3020 
3021         boolean foundPrimary = false;
3022 
3023         final ArrayList<StorageVolume> res = new ArrayList<>();
3024         synchronized (mLock) {
3025             for (int i = 0; i < mVolumes.size(); i++) {
3026                 final VolumeInfo vol = mVolumes.valueAt(i);
3027                 switch (vol.getType()) {
3028                     case VolumeInfo.TYPE_PUBLIC:
3029                     case VolumeInfo.TYPE_EMULATED:
3030                         break;
3031                     default:
3032                         continue;
3033                 }
3034 
3035                 boolean match = false;
3036                 if (forWrite) {
3037                     match = vol.isVisibleForWrite(userId);
3038                 } else {
3039                     match = vol.isVisibleForRead(userId)
3040                             || (includeInvisible && vol.getPath() != null);
3041                 }
3042                 if (!match) continue;
3043 
3044                 boolean reportUnmounted = false;
3045                 if ((vol.getType() == VolumeInfo.TYPE_EMULATED) && !userKeyUnlocked) {
3046                     reportUnmounted = true;
3047                 } else if (!storagePermission && !realState) {
3048                     reportUnmounted = true;
3049                 }
3050 
3051                 final StorageVolume userVol = vol.buildStorageVolume(mContext, userId,
3052                         reportUnmounted);
3053                 if (vol.isPrimary()) {
3054                     res.add(0, userVol);
3055                     foundPrimary = true;
3056                 } else {
3057                     res.add(userVol);
3058                 }
3059             }
3060         }
3061 
3062         if (!foundPrimary) {
3063             Log.w(TAG, "No primary storage defined yet; hacking together a stub");
3064 
3065             final boolean primaryPhysical = SystemProperties.getBoolean(
3066                     StorageManager.PROP_PRIMARY_PHYSICAL, false);
3067 
3068             final String id = "stub_primary";
3069             final File path = Environment.getLegacyExternalStorageDirectory();
3070             final String description = mContext.getString(android.R.string.unknownName);
3071             final boolean primary = true;
3072             final boolean removable = primaryPhysical;
3073             final boolean emulated = !primaryPhysical;
3074             final long mtpReserveSize = 0L;
3075             final boolean allowMassStorage = false;
3076             final long maxFileSize = 0L;
3077             final UserHandle owner = new UserHandle(userId);
3078             final String uuid = null;
3079             final String state = Environment.MEDIA_REMOVED;
3080 
3081             res.add(0, new StorageVolume(id, StorageVolume.STORAGE_ID_INVALID, path,
3082                     description, primary, removable, emulated, mtpReserveSize,
3083                     allowMassStorage, maxFileSize, owner, uuid, state));
3084         }
3085 
3086         return res.toArray(new StorageVolume[res.size()]);
3087     }
3088 
3089     @Override
3090     public DiskInfo[] getDisks() {
3091         synchronized (mLock) {
3092             final DiskInfo[] res = new DiskInfo[mDisks.size()];
3093             for (int i = 0; i < mDisks.size(); i++) {
3094                 res[i] = mDisks.valueAt(i);
3095             }
3096             return res;
3097         }
3098     }
3099 
3100     @Override
3101     public VolumeInfo[] getVolumes(int flags) {
3102         synchronized (mLock) {
3103             final VolumeInfo[] res = new VolumeInfo[mVolumes.size()];
3104             for (int i = 0; i < mVolumes.size(); i++) {
3105                 res[i] = mVolumes.valueAt(i);
3106             }
3107             return res;
3108         }
3109     }
3110 
3111     @Override
3112     public VolumeRecord[] getVolumeRecords(int flags) {
3113         synchronized (mLock) {
3114             final VolumeRecord[] res = new VolumeRecord[mRecords.size()];
3115             for (int i = 0; i < mRecords.size(); i++) {
3116                 res[i] = mRecords.valueAt(i);
3117             }
3118             return res;
3119         }
3120     }
3121 
3122     private void addObbStateLocked(ObbState obbState) throws RemoteException {
3123         final IBinder binder = obbState.getBinder();
3124         List<ObbState> obbStates = mObbMounts.get(binder);
3125 
3126         if (obbStates == null) {
3127             obbStates = new ArrayList<ObbState>();
3128             mObbMounts.put(binder, obbStates);
3129         } else {
3130             for (final ObbState o : obbStates) {
3131                 if (o.rawPath.equals(obbState.rawPath)) {
3132                     throw new IllegalStateException("Attempt to add ObbState twice. "
3133                             + "This indicates an error in the MountService logic.");
3134                 }
3135             }
3136         }
3137 
3138         obbStates.add(obbState);
3139         try {
3140             obbState.link();
3141         } catch (RemoteException e) {
3142             /*
3143              * The binder died before we could link it, so clean up our state
3144              * and return failure.
3145              */
3146             obbStates.remove(obbState);
3147             if (obbStates.isEmpty()) {
3148                 mObbMounts.remove(binder);
3149             }
3150 
3151             // Rethrow the error so mountObb can get it
3152             throw e;
3153         }
3154 
3155         mObbPathToStateMap.put(obbState.rawPath, obbState);
3156     }
3157 
3158     private void removeObbStateLocked(ObbState obbState) {
3159         final IBinder binder = obbState.getBinder();
3160         final List<ObbState> obbStates = mObbMounts.get(binder);
3161         if (obbStates != null) {
3162             if (obbStates.remove(obbState)) {
3163                 obbState.unlink();
3164             }
3165             if (obbStates.isEmpty()) {
3166                 mObbMounts.remove(binder);
3167             }
3168         }
3169 
3170         mObbPathToStateMap.remove(obbState.rawPath);
3171     }
3172 
3173     private class ObbActionHandler extends Handler {
3174         private boolean mBound = false;
3175         private final List<ObbAction> mActions = new LinkedList<ObbAction>();
3176 
3177         ObbActionHandler(Looper l) {
3178             super(l);
3179         }
3180 
3181         @Override
3182         public void handleMessage(Message msg) {
3183             switch (msg.what) {
3184                 case OBB_RUN_ACTION: {
3185                     final ObbAction action = (ObbAction) msg.obj;
3186 
3187                     if (DEBUG_OBB)
3188                         Slog.i(TAG, "OBB_RUN_ACTION: " + action.toString());
3189 
3190                     // If a bind was already initiated we don't really
3191                     // need to do anything. The pending install
3192                     // will be processed later on.
3193                     if (!mBound) {
3194                         // If this is the only one pending we might
3195                         // have to bind to the service again.
3196                         if (!connectToService()) {
3197                             Slog.e(TAG, "Failed to bind to media container service");
3198                             action.handleError();
3199                             return;
3200                         }
3201                     }
3202 
3203                     mActions.add(action);
3204                     break;
3205                 }
3206                 case OBB_MCS_BOUND: {
3207                     if (DEBUG_OBB)
3208                         Slog.i(TAG, "OBB_MCS_BOUND");
3209                     if (msg.obj != null) {
3210                         mContainerService = (IMediaContainerService) msg.obj;
3211                     }
3212                     if (mContainerService == null) {
3213                         // Something seriously wrong. Bail out
3214                         Slog.e(TAG, "Cannot bind to media container service");
3215                         for (ObbAction action : mActions) {
3216                             // Indicate service bind error
3217                             action.handleError();
3218                         }
3219                         mActions.clear();
3220                     } else if (mActions.size() > 0) {
3221                         final ObbAction action = mActions.get(0);
3222                         if (action != null) {
3223                             action.execute(this);
3224                         }
3225                     } else {
3226                         // Should never happen ideally.
3227                         Slog.w(TAG, "Empty queue");
3228                     }
3229                     break;
3230                 }
3231                 case OBB_MCS_RECONNECT: {
3232                     if (DEBUG_OBB)
3233                         Slog.i(TAG, "OBB_MCS_RECONNECT");
3234                     if (mActions.size() > 0) {
3235                         if (mBound) {
3236                             disconnectService();
3237                         }
3238                         if (!connectToService()) {
3239                             Slog.e(TAG, "Failed to bind to media container service");
3240                             for (ObbAction action : mActions) {
3241                                 // Indicate service bind error
3242                                 action.handleError();
3243                             }
3244                             mActions.clear();
3245                         }
3246                     }
3247                     break;
3248                 }
3249                 case OBB_MCS_UNBIND: {
3250                     if (DEBUG_OBB)
3251                         Slog.i(TAG, "OBB_MCS_UNBIND");
3252 
3253                     // Delete pending install
3254                     if (mActions.size() > 0) {
3255                         mActions.remove(0);
3256                     }
3257                     if (mActions.size() == 0) {
3258                         if (mBound) {
3259                             disconnectService();
3260                         }
3261                     } else {
3262                         // There are more pending requests in queue.
3263                         // Just post MCS_BOUND message to trigger processing
3264                         // of next pending install.
3265                         mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND);
3266                     }
3267                     break;
3268                 }
3269                 case OBB_FLUSH_MOUNT_STATE: {
3270                     final String path = (String) msg.obj;
3271 
3272                     if (DEBUG_OBB)
3273                         Slog.i(TAG, "Flushing all OBB state for path " + path);
3274 
3275                     synchronized (mObbMounts) {
3276                         final List<ObbState> obbStatesToRemove = new LinkedList<ObbState>();
3277 
3278                         final Iterator<ObbState> i = mObbPathToStateMap.values().iterator();
3279                         while (i.hasNext()) {
3280                             final ObbState state = i.next();
3281 
3282                             /*
3283                              * If this entry's source file is in the volume path
3284                              * that got unmounted, remove it because it's no
3285                              * longer valid.
3286                              */
3287                             if (state.canonicalPath.startsWith(path)) {
3288                                 obbStatesToRemove.add(state);
3289                             }
3290                         }
3291 
3292                         for (final ObbState obbState : obbStatesToRemove) {
3293                             if (DEBUG_OBB)
3294                                 Slog.i(TAG, "Removing state for " + obbState.rawPath);
3295 
3296                             removeObbStateLocked(obbState);
3297 
3298                             try {
3299                                 obbState.token.onObbResult(obbState.rawPath, obbState.nonce,
3300                                         OnObbStateChangeListener.UNMOUNTED);
3301                             } catch (RemoteException e) {
3302                                 Slog.i(TAG, "Couldn't send unmount notification for  OBB: "
3303                                         + obbState.rawPath);
3304                             }
3305                         }
3306                     }
3307                     break;
3308                 }
3309             }
3310         }
3311 
3312         private boolean connectToService() {
3313             if (DEBUG_OBB)
3314                 Slog.i(TAG, "Trying to bind to DefaultContainerService");
3315 
3316             Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
3317             if (mContext.bindServiceAsUser(service, mDefContainerConn, Context.BIND_AUTO_CREATE,
3318                     UserHandle.SYSTEM)) {
3319                 mBound = true;
3320                 return true;
3321             }
3322             return false;
3323         }
3324 
3325         private void disconnectService() {
3326             mContainerService = null;
3327             mBound = false;
3328             mContext.unbindService(mDefContainerConn);
3329         }
3330     }
3331 
3332     abstract class ObbAction {
3333         private static final int MAX_RETRIES = 3;
3334         private int mRetries;
3335 
3336         ObbState mObbState;
3337 
3338         ObbAction(ObbState obbState) {
3339             mObbState = obbState;
3340         }
3341 
3342         public void execute(ObbActionHandler handler) {
3343             try {
3344                 if (DEBUG_OBB)
3345                     Slog.i(TAG, "Starting to execute action: " + toString());
3346                 mRetries++;
3347                 if (mRetries > MAX_RETRIES) {
3348                     Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
3349                     mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
3350                     handleError();
3351                 } else {
3352                     handleExecute();
3353                     if (DEBUG_OBB)
3354                         Slog.i(TAG, "Posting install MCS_UNBIND");
3355                     mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
3356                 }
3357             } catch (RemoteException e) {
3358                 if (DEBUG_OBB)
3359                     Slog.i(TAG, "Posting install MCS_RECONNECT");
3360                 mObbActionHandler.sendEmptyMessage(OBB_MCS_RECONNECT);
3361             } catch (Exception e) {
3362                 if (DEBUG_OBB)
3363                     Slog.d(TAG, "Error handling OBB action", e);
3364                 handleError();
3365                 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
3366             }
3367         }
3368 
3369         abstract void handleExecute() throws RemoteException, IOException;
3370         abstract void handleError();
3371 
3372         protected ObbInfo getObbInfo() throws IOException {
3373             ObbInfo obbInfo;
3374             try {
3375                 obbInfo = mContainerService.getObbInfo(mObbState.canonicalPath);
3376             } catch (RemoteException e) {
3377                 Slog.d(TAG, "Couldn't call DefaultContainerService to fetch OBB info for "
3378                         + mObbState.canonicalPath);
3379                 obbInfo = null;
3380             }
3381             if (obbInfo == null) {
3382                 throw new IOException("Couldn't read OBB file: " + mObbState.canonicalPath);
3383             }
3384             return obbInfo;
3385         }
3386 
3387         protected void sendNewStatusOrIgnore(int status) {
3388             if (mObbState == null || mObbState.token == null) {
3389                 return;
3390             }
3391 
3392             try {
3393                 mObbState.token.onObbResult(mObbState.rawPath, mObbState.nonce, status);
3394             } catch (RemoteException e) {
3395                 Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
3396             }
3397         }
3398     }
3399 
3400     class MountObbAction extends ObbAction {
3401         private final String mKey;
3402         private final int mCallingUid;
3403 
3404         MountObbAction(ObbState obbState, String key, int callingUid) {
3405             super(obbState);
3406             mKey = key;
3407             mCallingUid = callingUid;
3408         }
3409 
3410         @Override
3411         public void handleExecute() throws IOException, RemoteException {
3412             waitForReady();
3413             warnOnNotMounted();
3414 
3415             final ObbInfo obbInfo = getObbInfo();
3416 
3417             if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mCallingUid)) {
3418                 Slog.w(TAG, "Denied attempt to mount OBB " + obbInfo.filename
3419                         + " which is owned by " + obbInfo.packageName);
3420                 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
3421                 return;
3422             }
3423 
3424             final boolean isMounted;
3425             synchronized (mObbMounts) {
3426                 isMounted = mObbPathToStateMap.containsKey(mObbState.rawPath);
3427             }
3428             if (isMounted) {
3429                 Slog.w(TAG, "Attempt to mount OBB which is already mounted: " + obbInfo.filename);
3430                 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_ALREADY_MOUNTED);
3431                 return;
3432             }
3433 
3434             final String hashedKey;
3435             if (mKey == null) {
3436                 hashedKey = "none";
3437             } else {
3438                 try {
3439                     SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
3440 
3441                     KeySpec ks = new PBEKeySpec(mKey.toCharArray(), obbInfo.salt,
3442                             PBKDF2_HASH_ROUNDS, CRYPTO_ALGORITHM_KEY_SIZE);
3443                     SecretKey key = factory.generateSecret(ks);
3444                     BigInteger bi = new BigInteger(key.getEncoded());
3445                     hashedKey = bi.toString(16);
3446                 } catch (NoSuchAlgorithmException e) {
3447                     Slog.e(TAG, "Could not load PBKDF2 algorithm", e);
3448                     sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
3449                     return;
3450                 } catch (InvalidKeySpecException e) {
3451                     Slog.e(TAG, "Invalid key spec when loading PBKDF2 algorithm", e);
3452                     sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
3453                     return;
3454                 }
3455             }
3456 
3457             int rc = StorageResultCode.OperationSucceeded;
3458             try {
3459                 mConnector.execute("obb", "mount", mObbState.canonicalPath, new SensitiveArg(hashedKey),
3460                         mObbState.ownerGid);
3461             } catch (NativeDaemonConnectorException e) {
3462                 int code = e.getCode();
3463                 if (code != VoldResponseCode.OpFailedStorageBusy) {
3464                     rc = StorageResultCode.OperationFailedInternalError;
3465                 }
3466             }
3467 
3468             if (rc == StorageResultCode.OperationSucceeded) {
3469                 if (DEBUG_OBB)
3470                     Slog.d(TAG, "Successfully mounted OBB " + mObbState.canonicalPath);
3471 
3472                 synchronized (mObbMounts) {
3473                     addObbStateLocked(mObbState);
3474                 }
3475 
3476                 sendNewStatusOrIgnore(OnObbStateChangeListener.MOUNTED);
3477             } else {
3478                 Slog.e(TAG, "Couldn't mount OBB file: " + rc);
3479 
3480                 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT);
3481             }
3482         }
3483 
3484         @Override
3485         public void handleError() {
3486             sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
3487         }
3488 
3489         @Override
3490         public String toString() {
3491             StringBuilder sb = new StringBuilder();
3492             sb.append("MountObbAction{");
3493             sb.append(mObbState);
3494             sb.append('}');
3495             return sb.toString();
3496         }
3497     }
3498 
3499     class UnmountObbAction extends ObbAction {
3500         private final boolean mForceUnmount;
3501 
3502         UnmountObbAction(ObbState obbState, boolean force) {
3503             super(obbState);
3504             mForceUnmount = force;
3505         }
3506 
3507         @Override
3508         public void handleExecute() throws IOException {
3509             waitForReady();
3510             warnOnNotMounted();
3511 
3512             final ObbState existingState;
3513             synchronized (mObbMounts) {
3514                 existingState = mObbPathToStateMap.get(mObbState.rawPath);
3515             }
3516 
3517             if (existingState == null) {
3518                 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_NOT_MOUNTED);
3519                 return;
3520             }
3521 
3522             if (existingState.ownerGid != mObbState.ownerGid) {
3523                 Slog.w(TAG, "Permission denied attempting to unmount OBB " + existingState.rawPath
3524                         + " (owned by GID " + existingState.ownerGid + ")");
3525                 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
3526                 return;
3527             }
3528 
3529             int rc = StorageResultCode.OperationSucceeded;
3530             try {
3531                 final Command cmd = new Command("obb", "unmount", mObbState.canonicalPath);
3532                 if (mForceUnmount) {
3533                     cmd.appendArg("force");
3534                 }
3535                 mConnector.execute(cmd);
3536             } catch (NativeDaemonConnectorException e) {
3537                 int code = e.getCode();
3538                 if (code == VoldResponseCode.OpFailedStorageBusy) {
3539                     rc = StorageResultCode.OperationFailedStorageBusy;
3540                 } else if (code == VoldResponseCode.OpFailedStorageNotFound) {
3541                     // If it's not mounted then we've already won.
3542                     rc = StorageResultCode.OperationSucceeded;
3543                 } else {
3544                     rc = StorageResultCode.OperationFailedInternalError;
3545                 }
3546             }
3547 
3548             if (rc == StorageResultCode.OperationSucceeded) {
3549                 synchronized (mObbMounts) {
3550                     removeObbStateLocked(existingState);
3551                 }
3552 
3553                 sendNewStatusOrIgnore(OnObbStateChangeListener.UNMOUNTED);
3554             } else {
3555                 Slog.w(TAG, "Could not unmount OBB: " + existingState);
3556                 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_UNMOUNT);
3557             }
3558         }
3559 
3560         @Override
3561         public void handleError() {
3562             sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
3563         }
3564 
3565         @Override
3566         public String toString() {
3567             StringBuilder sb = new StringBuilder();
3568             sb.append("UnmountObbAction{");
3569             sb.append(mObbState);
3570             sb.append(",force=");
3571             sb.append(mForceUnmount);
3572             sb.append('}');
3573             return sb.toString();
3574         }
3575     }
3576 
3577     private static class Callbacks extends Handler {
3578         private static final int MSG_STORAGE_STATE_CHANGED = 1;
3579         private static final int MSG_VOLUME_STATE_CHANGED = 2;
3580         private static final int MSG_VOLUME_RECORD_CHANGED = 3;
3581         private static final int MSG_VOLUME_FORGOTTEN = 4;
3582         private static final int MSG_DISK_SCANNED = 5;
3583         private static final int MSG_DISK_DESTROYED = 6;
3584 
3585         private final RemoteCallbackList<IMountServiceListener>
3586                 mCallbacks = new RemoteCallbackList<>();
3587 
3588         public Callbacks(Looper looper) {
3589             super(looper);
3590         }
3591 
3592         public void register(IMountServiceListener callback) {
3593             mCallbacks.register(callback);
3594         }
3595 
3596         public void unregister(IMountServiceListener callback) {
3597             mCallbacks.unregister(callback);
3598         }
3599 
3600         @Override
3601         public void handleMessage(Message msg) {
3602             final SomeArgs args = (SomeArgs) msg.obj;
3603             final int n = mCallbacks.beginBroadcast();
3604             for (int i = 0; i < n; i++) {
3605                 final IMountServiceListener callback = mCallbacks.getBroadcastItem(i);
3606                 try {
3607                     invokeCallback(callback, msg.what, args);
3608                 } catch (RemoteException ignored) {
3609                 }
3610             }
3611             mCallbacks.finishBroadcast();
3612             args.recycle();
3613         }
3614 
3615         private void invokeCallback(IMountServiceListener callback, int what, SomeArgs args)
3616                 throws RemoteException {
3617             switch (what) {
3618                 case MSG_STORAGE_STATE_CHANGED: {
3619                     callback.onStorageStateChanged((String) args.arg1, (String) args.arg2,
3620                             (String) args.arg3);
3621                     break;
3622                 }
3623                 case MSG_VOLUME_STATE_CHANGED: {
3624                     callback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3);
3625                     break;
3626                 }
3627                 case MSG_VOLUME_RECORD_CHANGED: {
3628                     callback.onVolumeRecordChanged((VolumeRecord) args.arg1);
3629                     break;
3630                 }
3631                 case MSG_VOLUME_FORGOTTEN: {
3632                     callback.onVolumeForgotten((String) args.arg1);
3633                     break;
3634                 }
3635                 case MSG_DISK_SCANNED: {
3636                     callback.onDiskScanned((DiskInfo) args.arg1, args.argi2);
3637                     break;
3638                 }
3639                 case MSG_DISK_DESTROYED: {
3640                     callback.onDiskDestroyed((DiskInfo) args.arg1);
3641                     break;
3642                 }
3643             }
3644         }
3645 
3646         private void notifyStorageStateChanged(String path, String oldState, String newState) {
3647             final SomeArgs args = SomeArgs.obtain();
3648             args.arg1 = path;
3649             args.arg2 = oldState;
3650             args.arg3 = newState;
3651             obtainMessage(MSG_STORAGE_STATE_CHANGED, args).sendToTarget();
3652         }
3653 
3654         private void notifyVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
3655             final SomeArgs args = SomeArgs.obtain();
3656             args.arg1 = vol.clone();
3657             args.argi2 = oldState;
3658             args.argi3 = newState;
3659             obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget();
3660         }
3661 
3662         private void notifyVolumeRecordChanged(VolumeRecord rec) {
3663             final SomeArgs args = SomeArgs.obtain();
3664             args.arg1 = rec.clone();
3665             obtainMessage(MSG_VOLUME_RECORD_CHANGED, args).sendToTarget();
3666         }
3667 
3668         private void notifyVolumeForgotten(String fsUuid) {
3669             final SomeArgs args = SomeArgs.obtain();
3670             args.arg1 = fsUuid;
3671             obtainMessage(MSG_VOLUME_FORGOTTEN, args).sendToTarget();
3672         }
3673 
3674         private void notifyDiskScanned(DiskInfo disk, int volumeCount) {
3675             final SomeArgs args = SomeArgs.obtain();
3676             args.arg1 = disk.clone();
3677             args.argi2 = volumeCount;
3678             obtainMessage(MSG_DISK_SCANNED, args).sendToTarget();
3679         }
3680 
3681         private void notifyDiskDestroyed(DiskInfo disk) {
3682             final SomeArgs args = SomeArgs.obtain();
3683             args.arg1 = disk.clone();
3684             obtainMessage(MSG_DISK_DESTROYED, args).sendToTarget();
3685         }
3686     }
3687 
3688     @Override
3689     protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
3690         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
3691 
3692         final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ", 160);
3693         synchronized (mLock) {
3694             pw.println("Disks:");
3695             pw.increaseIndent();
3696             for (int i = 0; i < mDisks.size(); i++) {
3697                 final DiskInfo disk = mDisks.valueAt(i);
3698                 disk.dump(pw);
3699             }
3700             pw.decreaseIndent();
3701 
3702             pw.println();
3703             pw.println("Volumes:");
3704             pw.increaseIndent();
3705             for (int i = 0; i < mVolumes.size(); i++) {
3706                 final VolumeInfo vol = mVolumes.valueAt(i);
3707                 if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.id)) continue;
3708                 vol.dump(pw);
3709             }
3710             pw.decreaseIndent();
3711 
3712             pw.println();
3713             pw.println("Records:");
3714             pw.increaseIndent();
3715             for (int i = 0; i < mRecords.size(); i++) {
3716                 final VolumeRecord note = mRecords.valueAt(i);
3717                 note.dump(pw);
3718             }
3719             pw.decreaseIndent();
3720 
3721             pw.println();
3722             pw.println("Primary storage UUID: " + mPrimaryStorageUuid);
3723             pw.println("Force adoptable: " + mForceAdoptable);
3724             pw.println();
3725             pw.println("Local unlocked users: " + Arrays.toString(mLocalUnlockedUsers));
3726             pw.println("System unlocked users: " + Arrays.toString(mSystemUnlockedUsers));
3727         }
3728 
3729         synchronized (mObbMounts) {
3730             pw.println();
3731             pw.println("mObbMounts:");
3732             pw.increaseIndent();
3733             final Iterator<Entry<IBinder, List<ObbState>>> binders = mObbMounts.entrySet()
3734                     .iterator();
3735             while (binders.hasNext()) {
3736                 Entry<IBinder, List<ObbState>> e = binders.next();
3737                 pw.println(e.getKey() + ":");
3738                 pw.increaseIndent();
3739                 final List<ObbState> obbStates = e.getValue();
3740                 for (final ObbState obbState : obbStates) {
3741                     pw.println(obbState);
3742                 }
3743                 pw.decreaseIndent();
3744             }
3745             pw.decreaseIndent();
3746 
3747             pw.println();
3748             pw.println("mObbPathToStateMap:");
3749             pw.increaseIndent();
3750             final Iterator<Entry<String, ObbState>> maps = mObbPathToStateMap.entrySet().iterator();
3751             while (maps.hasNext()) {
3752                 final Entry<String, ObbState> e = maps.next();
3753                 pw.print(e.getKey());
3754                 pw.print(" -> ");
3755                 pw.println(e.getValue());
3756             }
3757             pw.decreaseIndent();
3758         }
3759 
3760         pw.println();
3761         pw.println("mConnector:");
3762         pw.increaseIndent();
3763         mConnector.dump(fd, pw, args);
3764         pw.decreaseIndent();
3765 
3766         pw.println();
3767         pw.println("mCryptConnector:");
3768         pw.increaseIndent();
3769         mCryptConnector.dump(fd, pw, args);
3770         pw.decreaseIndent();
3771 
3772         pw.println();
3773         pw.print("Last maintenance: ");
3774         pw.println(TimeUtils.formatForLogging(mLastMaintenance));
3775     }
3776 
3777     /** {@inheritDoc} */
3778     @Override
3779     public void monitor() {
3780         if (mConnector != null) {
3781             mConnector.monitor();
3782         }
3783         if (mCryptConnector != null) {
3784             mCryptConnector.monitor();
3785         }
3786     }
3787 
3788     private final class MountServiceInternalImpl extends MountServiceInternal {
3789         // Not guarded by a lock.
3790         private final CopyOnWriteArrayList<ExternalStorageMountPolicy> mPolicies =
3791                 new CopyOnWriteArrayList<>();
3792 
3793         @Override
3794         public void addExternalStoragePolicy(ExternalStorageMountPolicy policy) {
3795             // No locking - CopyOnWriteArrayList
3796             mPolicies.add(policy);
3797         }
3798 
3799         @Override
3800         public void onExternalStoragePolicyChanged(int uid, String packageName) {
3801             final int mountMode = getExternalStorageMountMode(uid, packageName);
3802             remountUidExternalStorage(uid, mountMode);
3803         }
3804 
3805         @Override
3806         public int getExternalStorageMountMode(int uid, String packageName) {
3807             // No locking - CopyOnWriteArrayList
3808             int mountMode = Integer.MAX_VALUE;
3809             for (ExternalStorageMountPolicy policy : mPolicies) {
3810                 final int policyMode = policy.getMountMode(uid, packageName);
3811                 if (policyMode == Zygote.MOUNT_EXTERNAL_NONE) {
3812                     return Zygote.MOUNT_EXTERNAL_NONE;
3813                 }
3814                 mountMode = Math.min(mountMode, policyMode);
3815             }
3816             if (mountMode == Integer.MAX_VALUE) {
3817                 return Zygote.MOUNT_EXTERNAL_NONE;
3818             }
3819             return mountMode;
3820         }
3821 
3822         public boolean hasExternalStorage(int uid, String packageName) {
3823             // No need to check for system uid. This avoids a deadlock between
3824             // PackageManagerService and AppOpsService.
3825             if (uid == Process.SYSTEM_UID) {
3826                 return true;
3827             }
3828             // No locking - CopyOnWriteArrayList
3829             for (ExternalStorageMountPolicy policy : mPolicies) {
3830                 final boolean policyHasStorage = policy.hasExternalStorage(uid, packageName);
3831                 if (!policyHasStorage) {
3832                     return false;
3833                 }
3834             }
3835             return true;
3836         }
3837     }
3838 }
3839