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
17package com.android.server;
18
19import static com.android.internal.util.XmlUtils.readBooleanAttribute;
20import static com.android.internal.util.XmlUtils.readIntAttribute;
21import static com.android.internal.util.XmlUtils.readLongAttribute;
22import static com.android.internal.util.XmlUtils.readStringAttribute;
23import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
24import static com.android.internal.util.XmlUtils.writeIntAttribute;
25import static com.android.internal.util.XmlUtils.writeLongAttribute;
26import static com.android.internal.util.XmlUtils.writeStringAttribute;
27
28import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
29import static org.xmlpull.v1.XmlPullParser.START_TAG;
30
31import android.Manifest;
32import android.annotation.Nullable;
33import android.app.ActivityManager;
34import android.app.ActivityManagerNative;
35import android.app.AppOpsManager;
36import android.app.IActivityManager;
37import android.content.BroadcastReceiver;
38import android.content.ComponentName;
39import android.content.Context;
40import android.content.Intent;
41import android.content.IntentFilter;
42import android.content.ServiceConnection;
43import android.content.pm.IPackageMoveObserver;
44import android.content.pm.PackageManager;
45import android.content.pm.ProviderInfo;
46import android.content.pm.UserInfo;
47import android.content.res.Configuration;
48import android.content.res.ObbInfo;
49import android.net.Uri;
50import android.os.Binder;
51import android.os.DropBoxManager;
52import android.os.Environment;
53import android.os.Environment.UserEnvironment;
54import android.os.FileUtils;
55import android.os.Handler;
56import android.os.HandlerThread;
57import android.os.IBinder;
58import android.os.Looper;
59import android.os.Message;
60import android.os.ParcelFileDescriptor;
61import android.os.PowerManager;
62import android.os.Process;
63import android.os.RemoteCallbackList;
64import android.os.RemoteException;
65import android.os.ServiceManager;
66import android.os.SystemClock;
67import android.os.SystemProperties;
68import android.os.UserHandle;
69import android.os.UserManager;
70import android.os.storage.DiskInfo;
71import android.os.storage.IMountService;
72import android.os.storage.IMountServiceListener;
73import android.os.storage.IMountShutdownObserver;
74import android.os.storage.IObbActionListener;
75import android.os.storage.MountServiceInternal;
76import android.os.storage.OnObbStateChangeListener;
77import android.os.storage.StorageManager;
78import android.os.storage.StorageResultCode;
79import android.os.storage.StorageVolume;
80import android.os.storage.VolumeInfo;
81import android.os.storage.VolumeRecord;
82import android.provider.MediaStore;
83import android.provider.Settings;
84import android.text.TextUtils;
85import android.text.format.DateUtils;
86import android.util.ArrayMap;
87import android.util.AtomicFile;
88import android.util.Log;
89import android.util.Slog;
90import android.util.TimeUtils;
91import android.util.Xml;
92
93import com.android.internal.annotations.GuardedBy;
94import com.android.internal.app.IMediaContainerService;
95import com.android.internal.os.SomeArgs;
96import com.android.internal.os.Zygote;
97import com.android.internal.util.ArrayUtils;
98import com.android.internal.util.FastXmlSerializer;
99import com.android.internal.util.HexDump;
100import com.android.internal.util.IndentingPrintWriter;
101import com.android.internal.util.Preconditions;
102import com.android.internal.widget.LockPatternUtils;
103import com.android.server.NativeDaemonConnector.Command;
104import com.android.server.NativeDaemonConnector.SensitiveArg;
105import com.android.server.pm.PackageManagerService;
106
107import libcore.io.IoUtils;
108import libcore.util.EmptyArray;
109
110import org.xmlpull.v1.XmlPullParser;
111import org.xmlpull.v1.XmlPullParserException;
112import org.xmlpull.v1.XmlSerializer;
113
114import java.io.File;
115import java.io.FileDescriptor;
116import java.io.FileInputStream;
117import java.io.FileNotFoundException;
118import java.io.FileOutputStream;
119import java.io.IOException;
120import java.io.PrintWriter;
121import java.math.BigInteger;
122import java.nio.charset.StandardCharsets;
123import java.security.NoSuchAlgorithmException;
124import java.security.spec.InvalidKeySpecException;
125import java.security.spec.KeySpec;
126import java.util.ArrayList;
127import java.util.Arrays;
128import java.util.HashMap;
129import java.util.HashSet;
130import java.util.Iterator;
131import java.util.LinkedList;
132import java.util.List;
133import java.util.Locale;
134import java.util.Map;
135import java.util.Map.Entry;
136import java.util.Objects;
137import java.util.concurrent.CopyOnWriteArrayList;
138import java.util.concurrent.CountDownLatch;
139import java.util.concurrent.TimeUnit;
140import java.util.concurrent.TimeoutException;
141
142import javax.crypto.SecretKey;
143import javax.crypto.SecretKeyFactory;
144import 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 */
151class 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
160        public Lifecycle(Context context) {
161            super(context);
162        }
163
164        @Override
165        public void onStart() {
166            mMountService = new MountService(getContext());
167            publishBinderService("mount", mMountService);
168            mMountService.start();
169        }
170
171        @Override
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
181        public void onSwitchUser(int userHandle) {
182            mMountService.mCurrentUserId = userHandle;
183        }
184
185        @Override
186        public void onUnlockUser(int userHandle) {
187            mMountService.onUnlockUser(userHandle);
188        }
189
190        @Override
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
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
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
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
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
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
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
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
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 {
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
517        public IBinder getBinder() {
518            return token.asBinder();
519        }
520
521        @Override
522        public void binderDied() {
523            ObbAction action = new UnmountObbAction(this, true);
524            mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
525        }
526
527        public void link() throws RemoteException {
528            getBinder().linkToDeath(this, 0);
529        }
530
531        public void unlink() {
532            getBinder().unlinkToDeath(this, 0);
533        }
534
535        @Override
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
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
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 {
603        public MountServiceHandler(Looper looper) {
604            super(looper);
605        }
606
607        @Override
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
757    public void waitForAsecScan() {
758        waitForLatch(mAsecsScanned, "mAsecsScanned");
759    }
760
761    private void waitForReady() {
762        waitForLatch(mConnectedSignal, "mConnectedSignal");
763    }
764
765    private void waitForLatch(CountDownLatch latch, String condition) {
766        try {
767            waitForLatch(latch, condition, -1);
768        } catch (TimeoutException ignored) {
769        }
770    }
771
772    private void waitForLatch(CountDownLatch latch, String condition, long timeoutMillis)
773            throws TimeoutException {
774        final long startMillis = SystemClock.elapsedRealtime();
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
793    private boolean isReady() {
794        try {
795            return mConnectedSignal.await(0, TimeUnit.MILLISECONDS);
796        } catch (InterruptedException e) {
797            return false;
798        }
799    }
800
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
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
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
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
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
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
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
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
959    public void runMaintenance() {
960        enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
961        runIdleMaintenance(null);
962    }
963
964    @Override
965    public long lastMaintenance() {
966        return mLastMaintenance;
967    }
968
969    /**
970     * Callback from NativeDaemonConnector
971     */
972    @Override
973    public void onDaemonConnected() {
974        mDaemonConnected = true;
975        mHandler.obtainMessage(H_DAEMON_CONNECTED).sendToTarget();
976    }
977
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
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
1035    public boolean onCheckHoldWakeLock(int code) {
1036        return false;
1037    }
1038
1039    /**
1040     * Callback from NativeDaemonConnector
1041     */
1042    @Override
1043    public boolean onEvent(int code, String raw, String[] cooked) {
1044        synchronized (mLock) {
1045            return onEventLocked(code, raw, cooked);
1046        }
1047    }
1048
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                // Adoptable storage isn't currently supported on FBE devices
1060                if (StorageManager.isFileEncryptedNativeOnly()) {
1061                    flags &= ~DiskInfo.FLAG_ADOPTABLE;
1062                }
1063                mDisks.put(id, new DiskInfo(id, flags));
1064                break;
1065            }
1066            case VoldResponseCode.DISK_SIZE_CHANGED: {
1067                if (cooked.length != 3) break;
1068                final DiskInfo disk = mDisks.get(cooked[1]);
1069                if (disk != null) {
1070                    disk.size = Long.parseLong(cooked[2]);
1071                }
1072                break;
1073            }
1074            case VoldResponseCode.DISK_LABEL_CHANGED: {
1075                final DiskInfo disk = mDisks.get(cooked[1]);
1076                if (disk != null) {
1077                    final StringBuilder builder = new StringBuilder();
1078                    for (int i = 2; i < cooked.length; i++) {
1079                        builder.append(cooked[i]).append(' ');
1080                    }
1081                    disk.label = builder.toString().trim();
1082                }
1083                break;
1084            }
1085            case VoldResponseCode.DISK_SCANNED: {
1086                if (cooked.length != 2) break;
1087                final DiskInfo disk = mDisks.get(cooked[1]);
1088                if (disk != null) {
1089                    onDiskScannedLocked(disk);
1090                }
1091                break;
1092            }
1093            case VoldResponseCode.DISK_SYS_PATH_CHANGED: {
1094                if (cooked.length != 3) break;
1095                final DiskInfo disk = mDisks.get(cooked[1]);
1096                if (disk != null) {
1097                    disk.sysPath = cooked[2];
1098                }
1099                break;
1100            }
1101            case VoldResponseCode.DISK_DESTROYED: {
1102                if (cooked.length != 2) break;
1103                final DiskInfo disk = mDisks.remove(cooked[1]);
1104                if (disk != null) {
1105                    mCallbacks.notifyDiskDestroyed(disk);
1106                }
1107                break;
1108            }
1109
1110            case VoldResponseCode.VOLUME_CREATED: {
1111                final String id = cooked[1];
1112                final int type = Integer.parseInt(cooked[2]);
1113                final String diskId = TextUtils.nullIfEmpty(cooked[3]);
1114                final String partGuid = TextUtils.nullIfEmpty(cooked[4]);
1115
1116                final DiskInfo disk = mDisks.get(diskId);
1117                final VolumeInfo vol = new VolumeInfo(id, type, disk, partGuid);
1118                mVolumes.put(id, vol);
1119                onVolumeCreatedLocked(vol);
1120                break;
1121            }
1122            case VoldResponseCode.VOLUME_STATE_CHANGED: {
1123                if (cooked.length != 3) break;
1124                final VolumeInfo vol = mVolumes.get(cooked[1]);
1125                if (vol != null) {
1126                    final int oldState = vol.state;
1127                    final int newState = Integer.parseInt(cooked[2]);
1128                    vol.state = newState;
1129                    onVolumeStateChangedLocked(vol, oldState, newState);
1130                }
1131                break;
1132            }
1133            case VoldResponseCode.VOLUME_FS_TYPE_CHANGED: {
1134                if (cooked.length != 3) break;
1135                final VolumeInfo vol = mVolumes.get(cooked[1]);
1136                if (vol != null) {
1137                    vol.fsType = cooked[2];
1138                }
1139                break;
1140            }
1141            case VoldResponseCode.VOLUME_FS_UUID_CHANGED: {
1142                if (cooked.length != 3) break;
1143                final VolumeInfo vol = mVolumes.get(cooked[1]);
1144                if (vol != null) {
1145                    vol.fsUuid = cooked[2];
1146                }
1147                break;
1148            }
1149            case VoldResponseCode.VOLUME_FS_LABEL_CHANGED: {
1150                final VolumeInfo vol = mVolumes.get(cooked[1]);
1151                if (vol != null) {
1152                    final StringBuilder builder = new StringBuilder();
1153                    for (int i = 2; i < cooked.length; i++) {
1154                        builder.append(cooked[i]).append(' ');
1155                    }
1156                    vol.fsLabel = builder.toString().trim();
1157                }
1158                // TODO: notify listeners that label changed
1159                break;
1160            }
1161            case VoldResponseCode.VOLUME_PATH_CHANGED: {
1162                if (cooked.length != 3) break;
1163                final VolumeInfo vol = mVolumes.get(cooked[1]);
1164                if (vol != null) {
1165                    vol.path = cooked[2];
1166                }
1167                break;
1168            }
1169            case VoldResponseCode.VOLUME_INTERNAL_PATH_CHANGED: {
1170                if (cooked.length != 3) break;
1171                final VolumeInfo vol = mVolumes.get(cooked[1]);
1172                if (vol != null) {
1173                    vol.internalPath = cooked[2];
1174                }
1175                break;
1176            }
1177            case VoldResponseCode.VOLUME_DESTROYED: {
1178                if (cooked.length != 2) break;
1179                mVolumes.remove(cooked[1]);
1180                break;
1181            }
1182
1183            case VoldResponseCode.MOVE_STATUS: {
1184                final int status = Integer.parseInt(cooked[1]);
1185                onMoveStatusLocked(status);
1186                break;
1187            }
1188            case VoldResponseCode.BENCHMARK_RESULT: {
1189                if (cooked.length != 7) break;
1190                final String path = cooked[1];
1191                final String ident = cooked[2];
1192                final long create = Long.parseLong(cooked[3]);
1193                final long drop = Long.parseLong(cooked[4]);
1194                final long run = Long.parseLong(cooked[5]);
1195                final long destroy = Long.parseLong(cooked[6]);
1196
1197                final DropBoxManager dropBox = mContext.getSystemService(DropBoxManager.class);
1198                dropBox.addText(TAG_STORAGE_BENCHMARK, scrubPath(path)
1199                        + " " + ident + " " + create + " " + run + " " + destroy);
1200
1201                final VolumeRecord rec = findRecordForPath(path);
1202                if (rec != null) {
1203                    rec.lastBenchMillis = System.currentTimeMillis();
1204                    writeSettingsLocked();
1205                }
1206
1207                break;
1208            }
1209            case VoldResponseCode.TRIM_RESULT: {
1210                if (cooked.length != 4) break;
1211                final String path = cooked[1];
1212                final long bytes = Long.parseLong(cooked[2]);
1213                final long time = Long.parseLong(cooked[3]);
1214
1215                final DropBoxManager dropBox = mContext.getSystemService(DropBoxManager.class);
1216                dropBox.addText(TAG_STORAGE_TRIM, scrubPath(path)
1217                        + " " + bytes + " " + time);
1218
1219                final VolumeRecord rec = findRecordForPath(path);
1220                if (rec != null) {
1221                    rec.lastTrimMillis = System.currentTimeMillis();
1222                    writeSettingsLocked();
1223                }
1224
1225                break;
1226            }
1227
1228            default: {
1229                Slog.d(TAG, "Unhandled vold event " + code);
1230            }
1231        }
1232
1233        return true;
1234    }
1235
1236    private void onDiskScannedLocked(DiskInfo disk) {
1237        int volumeCount = 0;
1238        for (int i = 0; i < mVolumes.size(); i++) {
1239            final VolumeInfo vol = mVolumes.valueAt(i);
1240            if (Objects.equals(disk.id, vol.getDiskId())) {
1241                volumeCount++;
1242            }
1243        }
1244
1245        final Intent intent = new Intent(DiskInfo.ACTION_DISK_SCANNED);
1246        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
1247                | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
1248        intent.putExtra(DiskInfo.EXTRA_DISK_ID, disk.id);
1249        intent.putExtra(DiskInfo.EXTRA_VOLUME_COUNT, volumeCount);
1250        mHandler.obtainMessage(H_INTERNAL_BROADCAST, intent).sendToTarget();
1251
1252        final CountDownLatch latch = mDiskScanLatches.remove(disk.id);
1253        if (latch != null) {
1254            latch.countDown();
1255        }
1256
1257        disk.volumeCount = volumeCount;
1258        mCallbacks.notifyDiskScanned(disk, volumeCount);
1259    }
1260
1261    private void onVolumeCreatedLocked(VolumeInfo vol) {
1262        if (mPms.isOnlyCoreApps()) {
1263            Slog.d(TAG, "System booted in core-only mode; ignoring volume " + vol.getId());
1264            return;
1265        }
1266
1267        if (vol.type == VolumeInfo.TYPE_EMULATED) {
1268            final StorageManager storage = mContext.getSystemService(StorageManager.class);
1269            final VolumeInfo privateVol = storage.findPrivateForEmulated(vol);
1270
1271            if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, mPrimaryStorageUuid)
1272                    && VolumeInfo.ID_PRIVATE_INTERNAL.equals(privateVol.id)) {
1273                Slog.v(TAG, "Found primary storage at " + vol);
1274                vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
1275                vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
1276                mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
1277
1278            } else if (Objects.equals(privateVol.fsUuid, mPrimaryStorageUuid)) {
1279                Slog.v(TAG, "Found primary storage at " + vol);
1280                vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
1281                vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
1282                mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
1283            }
1284
1285        } else if (vol.type == VolumeInfo.TYPE_PUBLIC) {
1286            // TODO: only look at first public partition
1287            if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, mPrimaryStorageUuid)
1288                    && vol.disk.isDefaultPrimary()) {
1289                Slog.v(TAG, "Found primary storage at " + vol);
1290                vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
1291                vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
1292            }
1293
1294            // Adoptable public disks are visible to apps, since they meet
1295            // public API requirement of being in a stable location.
1296            if (vol.disk.isAdoptable()) {
1297                vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
1298            }
1299
1300            vol.mountUserId = mCurrentUserId;
1301            mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
1302
1303        } else if (vol.type == VolumeInfo.TYPE_PRIVATE) {
1304            mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
1305
1306        } else {
1307            Slog.d(TAG, "Skipping automatic mounting of " + vol);
1308        }
1309    }
1310
1311    private boolean isBroadcastWorthy(VolumeInfo vol) {
1312        switch (vol.getType()) {
1313            case VolumeInfo.TYPE_PRIVATE:
1314            case VolumeInfo.TYPE_PUBLIC:
1315            case VolumeInfo.TYPE_EMULATED:
1316                break;
1317            default:
1318                return false;
1319        }
1320
1321        switch (vol.getState()) {
1322            case VolumeInfo.STATE_MOUNTED:
1323            case VolumeInfo.STATE_MOUNTED_READ_ONLY:
1324            case VolumeInfo.STATE_EJECTING:
1325            case VolumeInfo.STATE_UNMOUNTED:
1326            case VolumeInfo.STATE_UNMOUNTABLE:
1327            case VolumeInfo.STATE_BAD_REMOVAL:
1328                break;
1329            default:
1330                return false;
1331        }
1332
1333        return true;
1334    }
1335
1336    private void onVolumeStateChangedLocked(VolumeInfo vol, int oldState, int newState) {
1337        // Remember that we saw this volume so we're ready to accept user
1338        // metadata, or so we can annoy them when a private volume is ejected
1339        if (vol.isMountedReadable() && !TextUtils.isEmpty(vol.fsUuid)) {
1340            VolumeRecord rec = mRecords.get(vol.fsUuid);
1341            if (rec == null) {
1342                rec = new VolumeRecord(vol.type, vol.fsUuid);
1343                rec.partGuid = vol.partGuid;
1344                rec.createdMillis = System.currentTimeMillis();
1345                if (vol.type == VolumeInfo.TYPE_PRIVATE) {
1346                    rec.nickname = vol.disk.getDescription();
1347                }
1348                mRecords.put(rec.fsUuid, rec);
1349                writeSettingsLocked();
1350            } else {
1351                // Handle upgrade case where we didn't store partition GUID
1352                if (TextUtils.isEmpty(rec.partGuid)) {
1353                    rec.partGuid = vol.partGuid;
1354                    writeSettingsLocked();
1355                }
1356            }
1357        }
1358
1359        mCallbacks.notifyVolumeStateChanged(vol, oldState, newState);
1360
1361        // Do not broadcast before boot has completed to avoid launching the
1362        // processes that receive the intent unnecessarily.
1363        if (mBootCompleted && isBroadcastWorthy(vol)) {
1364            final Intent intent = new Intent(VolumeInfo.ACTION_VOLUME_STATE_CHANGED);
1365            intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.id);
1366            intent.putExtra(VolumeInfo.EXTRA_VOLUME_STATE, newState);
1367            intent.putExtra(VolumeRecord.EXTRA_FS_UUID, vol.fsUuid);
1368            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
1369                    | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
1370            mHandler.obtainMessage(H_INTERNAL_BROADCAST, intent).sendToTarget();
1371        }
1372
1373        final String oldStateEnv = VolumeInfo.getEnvironmentForState(oldState);
1374        final String newStateEnv = VolumeInfo.getEnvironmentForState(newState);
1375
1376        if (!Objects.equals(oldStateEnv, newStateEnv)) {
1377            // Kick state changed event towards all started users. Any users
1378            // started after this point will trigger additional
1379            // user-specific broadcasts.
1380            for (int userId : mSystemUnlockedUsers) {
1381                if (vol.isVisibleForRead(userId)) {
1382                    final StorageVolume userVol = vol.buildStorageVolume(mContext, userId, false);
1383                    mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget();
1384
1385                    mCallbacks.notifyStorageStateChanged(userVol.getPath(), oldStateEnv,
1386                            newStateEnv);
1387                }
1388            }
1389        }
1390
1391        if (vol.type == VolumeInfo.TYPE_PUBLIC && vol.state == VolumeInfo.STATE_EJECTING) {
1392            // TODO: this should eventually be handled by new ObbVolume state changes
1393            /*
1394             * Some OBBs might have been unmounted when this volume was
1395             * unmounted, so send a message to the handler to let it know to
1396             * remove those from the list of mounted OBBS.
1397             */
1398            mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(
1399                    OBB_FLUSH_MOUNT_STATE, vol.path));
1400        }
1401    }
1402
1403    private void onMoveStatusLocked(int status) {
1404        if (mMoveCallback == null) {
1405            Slog.w(TAG, "Odd, status but no move requested");
1406            return;
1407        }
1408
1409        // TODO: estimate remaining time
1410        try {
1411            mMoveCallback.onStatusChanged(-1, status, -1);
1412        } catch (RemoteException ignored) {
1413        }
1414
1415        // We've finished copying and we're about to clean up old data, so
1416        // remember that move was successful if we get rebooted
1417        if (status == MOVE_STATUS_COPY_FINISHED) {
1418            Slog.d(TAG, "Move to " + mMoveTargetUuid + " copy phase finshed; persisting");
1419
1420            mPrimaryStorageUuid = mMoveTargetUuid;
1421            writeSettingsLocked();
1422        }
1423
1424        if (PackageManager.isMoveStatusFinished(status)) {
1425            Slog.d(TAG, "Move to " + mMoveTargetUuid + " finished with status " + status);
1426
1427            mMoveCallback = null;
1428            mMoveTargetUuid = null;
1429        }
1430    }
1431
1432    private void enforcePermission(String perm) {
1433        mContext.enforceCallingOrSelfPermission(perm, perm);
1434    }
1435
1436    /**
1437     * Decide if volume is mountable per device policies.
1438     */
1439    private boolean isMountDisallowed(VolumeInfo vol) {
1440        UserManager userManager = mContext.getSystemService(UserManager.class);
1441
1442        boolean isUsbRestricted = false;
1443        if (vol.disk != null && vol.disk.isUsb()) {
1444            isUsbRestricted = userManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER,
1445                    Binder.getCallingUserHandle());
1446        }
1447
1448        boolean isTypeRestricted = false;
1449        if (vol.type == VolumeInfo.TYPE_PUBLIC || vol.type == VolumeInfo.TYPE_PRIVATE) {
1450            isTypeRestricted = userManager
1451                    .hasUserRestriction(UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA,
1452                    Binder.getCallingUserHandle());
1453        }
1454
1455        return isUsbRestricted || isTypeRestricted;
1456    }
1457
1458    private void enforceAdminUser() {
1459        UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
1460        final int callingUserId = UserHandle.getCallingUserId();
1461        boolean isAdmin;
1462        long token = Binder.clearCallingIdentity();
1463        try {
1464            isAdmin = um.getUserInfo(callingUserId).isAdmin();
1465        } finally {
1466            Binder.restoreCallingIdentity(token);
1467        }
1468        if (!isAdmin) {
1469            throw new SecurityException("Only admin users can adopt sd cards");
1470        }
1471    }
1472
1473    /**
1474     * Constructs a new MountService instance
1475     *
1476     * @param context  Binder context for this service
1477     */
1478    public MountService(Context context) {
1479        sSelf = this;
1480
1481        mContext = context;
1482        mCallbacks = new Callbacks(FgThread.get().getLooper());
1483        mLockPatternUtils = new LockPatternUtils(mContext);
1484
1485        // XXX: This will go away soon in favor of IMountServiceObserver
1486        mPms = (PackageManagerService) ServiceManager.getService("package");
1487
1488        HandlerThread hthread = new HandlerThread(TAG);
1489        hthread.start();
1490        mHandler = new MountServiceHandler(hthread.getLooper());
1491
1492        // Add OBB Action Handler to MountService thread.
1493        mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper());
1494
1495        // Initialize the last-fstrim tracking if necessary
1496        File dataDir = Environment.getDataDirectory();
1497        File systemDir = new File(dataDir, "system");
1498        mLastMaintenanceFile = new File(systemDir, LAST_FSTRIM_FILE);
1499        if (!mLastMaintenanceFile.exists()) {
1500            // Not setting mLastMaintenance here means that we will force an
1501            // fstrim during reboot following the OTA that installs this code.
1502            try {
1503                (new FileOutputStream(mLastMaintenanceFile)).close();
1504            } catch (IOException e) {
1505                Slog.e(TAG, "Unable to create fstrim record " + mLastMaintenanceFile.getPath());
1506            }
1507        } else {
1508            mLastMaintenance = mLastMaintenanceFile.lastModified();
1509        }
1510
1511        mSettingsFile = new AtomicFile(
1512                new File(Environment.getDataSystemDirectory(), "storage.xml"));
1513
1514        synchronized (mLock) {
1515            readSettingsLocked();
1516        }
1517
1518        LocalServices.addService(MountServiceInternal.class, mMountServiceInternal);
1519
1520        /*
1521         * Create the connection to vold with a maximum queue of twice the
1522         * amount of containers we'd ever expect to have. This keeps an
1523         * "asec list" from blocking a thread repeatedly.
1524         */
1525
1526        mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25,
1527                null);
1528        mConnector.setDebug(true);
1529        mConnector.setWarnIfHeld(mLock);
1530        mConnectorThread = new Thread(mConnector, VOLD_TAG);
1531
1532        // Reuse parameters from first connector since they are tested and safe
1533        mCryptConnector = new NativeDaemonConnector(this, "cryptd",
1534                MAX_CONTAINERS * 2, CRYPTD_TAG, 25, null);
1535        mCryptConnector.setDebug(true);
1536        mCryptConnectorThread = new Thread(mCryptConnector, CRYPTD_TAG);
1537
1538        final IntentFilter userFilter = new IntentFilter();
1539        userFilter.addAction(Intent.ACTION_USER_ADDED);
1540        userFilter.addAction(Intent.ACTION_USER_REMOVED);
1541        mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);
1542
1543        synchronized (mLock) {
1544            addInternalVolumeLocked();
1545        }
1546
1547        // Add ourself to the Watchdog monitors if enabled.
1548        if (WATCHDOG_ENABLE) {
1549            Watchdog.getInstance().addMonitor(this);
1550        }
1551    }
1552
1553    private void start() {
1554        mConnectorThread.start();
1555        mCryptConnectorThread.start();
1556    }
1557
1558    private void systemReady() {
1559        mSystemReady = true;
1560        mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget();
1561    }
1562
1563    private void bootCompleted() {
1564        mBootCompleted = true;
1565    }
1566
1567    private String getDefaultPrimaryStorageUuid() {
1568        if (SystemProperties.getBoolean(StorageManager.PROP_PRIMARY_PHYSICAL, false)) {
1569            return StorageManager.UUID_PRIMARY_PHYSICAL;
1570        } else {
1571            return StorageManager.UUID_PRIVATE_INTERNAL;
1572        }
1573    }
1574
1575    private void readSettingsLocked() {
1576        mRecords.clear();
1577        mPrimaryStorageUuid = getDefaultPrimaryStorageUuid();
1578        mForceAdoptable = false;
1579
1580        FileInputStream fis = null;
1581        try {
1582            fis = mSettingsFile.openRead();
1583            final XmlPullParser in = Xml.newPullParser();
1584            in.setInput(fis, StandardCharsets.UTF_8.name());
1585
1586            int type;
1587            while ((type = in.next()) != END_DOCUMENT) {
1588                if (type == START_TAG) {
1589                    final String tag = in.getName();
1590                    if (TAG_VOLUMES.equals(tag)) {
1591                        final int version = readIntAttribute(in, ATTR_VERSION, VERSION_INIT);
1592                        final boolean primaryPhysical = SystemProperties.getBoolean(
1593                                StorageManager.PROP_PRIMARY_PHYSICAL, false);
1594                        final boolean validAttr = (version >= VERSION_FIX_PRIMARY)
1595                                || (version >= VERSION_ADD_PRIMARY && !primaryPhysical);
1596                        if (validAttr) {
1597                            mPrimaryStorageUuid = readStringAttribute(in,
1598                                    ATTR_PRIMARY_STORAGE_UUID);
1599                        }
1600                        mForceAdoptable = readBooleanAttribute(in, ATTR_FORCE_ADOPTABLE, false);
1601
1602                    } else if (TAG_VOLUME.equals(tag)) {
1603                        final VolumeRecord rec = readVolumeRecord(in);
1604                        mRecords.put(rec.fsUuid, rec);
1605                    }
1606                }
1607            }
1608        } catch (FileNotFoundException e) {
1609            // Missing metadata is okay, probably first boot
1610        } catch (IOException e) {
1611            Slog.wtf(TAG, "Failed reading metadata", e);
1612        } catch (XmlPullParserException e) {
1613            Slog.wtf(TAG, "Failed reading metadata", e);
1614        } finally {
1615            IoUtils.closeQuietly(fis);
1616        }
1617    }
1618
1619    private void writeSettingsLocked() {
1620        FileOutputStream fos = null;
1621        try {
1622            fos = mSettingsFile.startWrite();
1623
1624            XmlSerializer out = new FastXmlSerializer();
1625            out.setOutput(fos, StandardCharsets.UTF_8.name());
1626            out.startDocument(null, true);
1627            out.startTag(null, TAG_VOLUMES);
1628            writeIntAttribute(out, ATTR_VERSION, VERSION_FIX_PRIMARY);
1629            writeStringAttribute(out, ATTR_PRIMARY_STORAGE_UUID, mPrimaryStorageUuid);
1630            writeBooleanAttribute(out, ATTR_FORCE_ADOPTABLE, mForceAdoptable);
1631            final int size = mRecords.size();
1632            for (int i = 0; i < size; i++) {
1633                final VolumeRecord rec = mRecords.valueAt(i);
1634                writeVolumeRecord(out, rec);
1635            }
1636            out.endTag(null, TAG_VOLUMES);
1637            out.endDocument();
1638
1639            mSettingsFile.finishWrite(fos);
1640        } catch (IOException e) {
1641            if (fos != null) {
1642                mSettingsFile.failWrite(fos);
1643            }
1644        }
1645    }
1646
1647    public static VolumeRecord readVolumeRecord(XmlPullParser in) throws IOException {
1648        final int type = readIntAttribute(in, ATTR_TYPE);
1649        final String fsUuid = readStringAttribute(in, ATTR_FS_UUID);
1650        final VolumeRecord meta = new VolumeRecord(type, fsUuid);
1651        meta.partGuid = readStringAttribute(in, ATTR_PART_GUID);
1652        meta.nickname = readStringAttribute(in, ATTR_NICKNAME);
1653        meta.userFlags = readIntAttribute(in, ATTR_USER_FLAGS);
1654        meta.createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS);
1655        meta.lastTrimMillis = readLongAttribute(in, ATTR_LAST_TRIM_MILLIS);
1656        meta.lastBenchMillis = readLongAttribute(in, ATTR_LAST_BENCH_MILLIS);
1657        return meta;
1658    }
1659
1660    public static void writeVolumeRecord(XmlSerializer out, VolumeRecord rec) throws IOException {
1661        out.startTag(null, TAG_VOLUME);
1662        writeIntAttribute(out, ATTR_TYPE, rec.type);
1663        writeStringAttribute(out, ATTR_FS_UUID, rec.fsUuid);
1664        writeStringAttribute(out, ATTR_PART_GUID, rec.partGuid);
1665        writeStringAttribute(out, ATTR_NICKNAME, rec.nickname);
1666        writeIntAttribute(out, ATTR_USER_FLAGS, rec.userFlags);
1667        writeLongAttribute(out, ATTR_CREATED_MILLIS, rec.createdMillis);
1668        writeLongAttribute(out, ATTR_LAST_TRIM_MILLIS, rec.lastTrimMillis);
1669        writeLongAttribute(out, ATTR_LAST_BENCH_MILLIS, rec.lastBenchMillis);
1670        out.endTag(null, TAG_VOLUME);
1671    }
1672
1673    /**
1674     * Exposed API calls below here
1675     */
1676
1677    @Override
1678    public void registerListener(IMountServiceListener listener) {
1679        mCallbacks.register(listener);
1680    }
1681
1682    @Override
1683    public void unregisterListener(IMountServiceListener listener) {
1684        mCallbacks.unregister(listener);
1685    }
1686
1687    @Override
1688    public void shutdown(final IMountShutdownObserver observer) {
1689        enforcePermission(android.Manifest.permission.SHUTDOWN);
1690
1691        Slog.i(TAG, "Shutting down");
1692        mHandler.obtainMessage(H_SHUTDOWN, observer).sendToTarget();
1693    }
1694
1695    @Override
1696    public boolean isUsbMassStorageConnected() {
1697        throw new UnsupportedOperationException();
1698    }
1699
1700    @Override
1701    public void setUsbMassStorageEnabled(boolean enable) {
1702        throw new UnsupportedOperationException();
1703    }
1704
1705    @Override
1706    public boolean isUsbMassStorageEnabled() {
1707        throw new UnsupportedOperationException();
1708    }
1709
1710    @Override
1711    public String getVolumeState(String mountPoint) {
1712        throw new UnsupportedOperationException();
1713    }
1714
1715    @Override
1716    public boolean isExternalStorageEmulated() {
1717        throw new UnsupportedOperationException();
1718    }
1719
1720    @Override
1721    public int mountVolume(String path) {
1722        mount(findVolumeIdForPathOrThrow(path));
1723        return 0;
1724    }
1725
1726    @Override
1727    public void unmountVolume(String path, boolean force, boolean removeEncryption) {
1728        unmount(findVolumeIdForPathOrThrow(path));
1729    }
1730
1731    @Override
1732    public int formatVolume(String path) {
1733        format(findVolumeIdForPathOrThrow(path));
1734        return 0;
1735    }
1736
1737    @Override
1738    public void mount(String volId) {
1739        enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1740        waitForReady();
1741
1742        final VolumeInfo vol = findVolumeByIdOrThrow(volId);
1743        if (isMountDisallowed(vol)) {
1744            throw new SecurityException("Mounting " + volId + " restricted by policy");
1745        }
1746        try {
1747            mConnector.execute("volume", "mount", vol.id, vol.mountFlags, vol.mountUserId);
1748        } catch (NativeDaemonConnectorException e) {
1749            throw e.rethrowAsParcelableException();
1750        }
1751    }
1752
1753    @Override
1754    public void unmount(String volId) {
1755        enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1756        waitForReady();
1757
1758        final VolumeInfo vol = findVolumeByIdOrThrow(volId);
1759
1760        // TODO: expand PMS to know about multiple volumes
1761        if (vol.isPrimaryPhysical()) {
1762            final long ident = Binder.clearCallingIdentity();
1763            try {
1764                synchronized (mUnmountLock) {
1765                    mUnmountSignal = new CountDownLatch(1);
1766                    mPms.updateExternalMediaStatus(false, true);
1767                    waitForLatch(mUnmountSignal, "mUnmountSignal");
1768                    mUnmountSignal = null;
1769                }
1770            } finally {
1771                Binder.restoreCallingIdentity(ident);
1772            }
1773        }
1774
1775        try {
1776            mConnector.execute("volume", "unmount", vol.id);
1777        } catch (NativeDaemonConnectorException e) {
1778            throw e.rethrowAsParcelableException();
1779        }
1780    }
1781
1782    @Override
1783    public void format(String volId) {
1784        enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
1785        waitForReady();
1786
1787        final VolumeInfo vol = findVolumeByIdOrThrow(volId);
1788        try {
1789            mConnector.execute("volume", "format", vol.id, "auto");
1790        } catch (NativeDaemonConnectorException e) {
1791            throw e.rethrowAsParcelableException();
1792        }
1793    }
1794
1795    @Override
1796    public long benchmark(String volId) {
1797        enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
1798        waitForReady();
1799
1800        try {
1801            // TODO: make benchmark async so we don't block other commands
1802            final NativeDaemonEvent res = mConnector.execute(3 * DateUtils.MINUTE_IN_MILLIS,
1803                    "volume", "benchmark", volId);
1804            return Long.parseLong(res.getMessage());
1805        } catch (NativeDaemonTimeoutException e) {
1806            return Long.MAX_VALUE;
1807        } catch (NativeDaemonConnectorException e) {
1808            throw e.rethrowAsParcelableException();
1809        }
1810    }
1811
1812    @Override
1813    public void partitionPublic(String diskId) {
1814        enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
1815        waitForReady();
1816
1817        final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
1818        try {
1819            mConnector.execute("volume", "partition", diskId, "public");
1820            waitForLatch(latch, "partitionPublic", 3 * DateUtils.MINUTE_IN_MILLIS);
1821        } catch (NativeDaemonConnectorException e) {
1822            throw e.rethrowAsParcelableException();
1823        } catch (TimeoutException e) {
1824            throw new IllegalStateException(e);
1825        }
1826    }
1827
1828    @Override
1829    public void partitionPrivate(String diskId) {
1830        enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
1831        enforceAdminUser();
1832        waitForReady();
1833
1834        final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
1835        try {
1836            mConnector.execute("volume", "partition", diskId, "private");
1837            waitForLatch(latch, "partitionPrivate", 3 * DateUtils.MINUTE_IN_MILLIS);
1838        } catch (NativeDaemonConnectorException e) {
1839            throw e.rethrowAsParcelableException();
1840        } catch (TimeoutException e) {
1841            throw new IllegalStateException(e);
1842        }
1843    }
1844
1845    @Override
1846    public void partitionMixed(String diskId, int ratio) {
1847        enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
1848        enforceAdminUser();
1849        waitForReady();
1850
1851        final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
1852        try {
1853            mConnector.execute("volume", "partition", diskId, "mixed", ratio);
1854            waitForLatch(latch, "partitionMixed", 3 * DateUtils.MINUTE_IN_MILLIS);
1855        } catch (NativeDaemonConnectorException e) {
1856            throw e.rethrowAsParcelableException();
1857        } catch (TimeoutException e) {
1858            throw new IllegalStateException(e);
1859        }
1860    }
1861
1862    @Override
1863    public void setVolumeNickname(String fsUuid, String nickname) {
1864        enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1865        waitForReady();
1866
1867        Preconditions.checkNotNull(fsUuid);
1868        synchronized (mLock) {
1869            final VolumeRecord rec = mRecords.get(fsUuid);
1870            rec.nickname = nickname;
1871            mCallbacks.notifyVolumeRecordChanged(rec);
1872            writeSettingsLocked();
1873        }
1874    }
1875
1876    @Override
1877    public void setVolumeUserFlags(String fsUuid, int flags, int mask) {
1878        enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1879        waitForReady();
1880
1881        Preconditions.checkNotNull(fsUuid);
1882        synchronized (mLock) {
1883            final VolumeRecord rec = mRecords.get(fsUuid);
1884            rec.userFlags = (rec.userFlags & ~mask) | (flags & mask);
1885            mCallbacks.notifyVolumeRecordChanged(rec);
1886            writeSettingsLocked();
1887        }
1888    }
1889
1890    @Override
1891    public void forgetVolume(String fsUuid) {
1892        enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1893        waitForReady();
1894
1895        Preconditions.checkNotNull(fsUuid);
1896
1897        synchronized (mLock) {
1898            final VolumeRecord rec = mRecords.remove(fsUuid);
1899            if (rec != null && !TextUtils.isEmpty(rec.partGuid)) {
1900                mHandler.obtainMessage(H_PARTITION_FORGET, rec.partGuid).sendToTarget();
1901            }
1902            mCallbacks.notifyVolumeForgotten(fsUuid);
1903
1904            // If this had been primary storage, revert back to internal and
1905            // reset vold so we bind into new volume into place.
1906            if (Objects.equals(mPrimaryStorageUuid, fsUuid)) {
1907                mPrimaryStorageUuid = getDefaultPrimaryStorageUuid();
1908                mHandler.obtainMessage(H_RESET).sendToTarget();
1909            }
1910
1911            writeSettingsLocked();
1912        }
1913    }
1914
1915    @Override
1916    public void forgetAllVolumes() {
1917        enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1918        waitForReady();
1919
1920        synchronized (mLock) {
1921            for (int i = 0; i < mRecords.size(); i++) {
1922                final String fsUuid = mRecords.keyAt(i);
1923                final VolumeRecord rec = mRecords.valueAt(i);
1924                if (!TextUtils.isEmpty(rec.partGuid)) {
1925                    mHandler.obtainMessage(H_PARTITION_FORGET, rec.partGuid).sendToTarget();
1926                }
1927                mCallbacks.notifyVolumeForgotten(fsUuid);
1928            }
1929            mRecords.clear();
1930
1931            if (!Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, mPrimaryStorageUuid)) {
1932                mPrimaryStorageUuid = getDefaultPrimaryStorageUuid();
1933            }
1934
1935            writeSettingsLocked();
1936            mHandler.obtainMessage(H_RESET).sendToTarget();
1937        }
1938    }
1939
1940    private void forgetPartition(String partGuid) {
1941        try {
1942            mConnector.execute("volume", "forget_partition", partGuid);
1943        } catch (NativeDaemonConnectorException e) {
1944            Slog.w(TAG, "Failed to forget key for " + partGuid + ": " + e);
1945        }
1946    }
1947
1948    private void remountUidExternalStorage(int uid, int mode) {
1949        waitForReady();
1950
1951        String modeName = "none";
1952        switch (mode) {
1953            case Zygote.MOUNT_EXTERNAL_DEFAULT: {
1954                modeName = "default";
1955            } break;
1956
1957            case Zygote.MOUNT_EXTERNAL_READ: {
1958                modeName = "read";
1959            } break;
1960
1961            case Zygote.MOUNT_EXTERNAL_WRITE: {
1962                modeName = "write";
1963            } break;
1964        }
1965
1966        try {
1967            mConnector.execute("volume", "remount_uid", uid, modeName);
1968        } catch (NativeDaemonConnectorException e) {
1969            Slog.w(TAG, "Failed to remount UID " + uid + " as " + modeName + ": " + e);
1970        }
1971    }
1972
1973    @Override
1974    public void setDebugFlags(int flags, int mask) {
1975        enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1976        waitForReady();
1977
1978        if ((mask & StorageManager.DEBUG_EMULATE_FBE) != 0) {
1979            if (StorageManager.isFileEncryptedNativeOnly()) {
1980                throw new IllegalStateException(
1981                        "Emulation not available on device with native FBE");
1982            }
1983            if (mLockPatternUtils.isCredentialRequiredToDecrypt(false)) {
1984                throw new IllegalStateException(
1985                        "Emulation requires disabling 'Secure start-up' in Settings > Security");
1986            }
1987
1988            final long token = Binder.clearCallingIdentity();
1989            try {
1990                final boolean emulateFbe = (flags & StorageManager.DEBUG_EMULATE_FBE) != 0;
1991                SystemProperties.set(StorageManager.PROP_EMULATE_FBE, Boolean.toString(emulateFbe));
1992
1993                // Perform hard reboot to kick policy into place
1994                mContext.getSystemService(PowerManager.class).reboot(null);
1995            } finally {
1996                Binder.restoreCallingIdentity(token);
1997            }
1998        }
1999
2000        if ((mask & StorageManager.DEBUG_FORCE_ADOPTABLE) != 0) {
2001            if (StorageManager.isFileEncryptedNativeOnly()) {
2002                throw new IllegalStateException(
2003                        "Adoptable storage not available on device with native FBE");
2004            }
2005
2006            synchronized (mLock) {
2007                mForceAdoptable = (flags & StorageManager.DEBUG_FORCE_ADOPTABLE) != 0;
2008
2009                writeSettingsLocked();
2010                mHandler.obtainMessage(H_RESET).sendToTarget();
2011            }
2012        }
2013
2014        if ((mask & (StorageManager.DEBUG_SDCARDFS_FORCE_ON
2015                | StorageManager.DEBUG_SDCARDFS_FORCE_OFF)) != 0) {
2016            final String value;
2017            if ((flags & StorageManager.DEBUG_SDCARDFS_FORCE_ON) != 0) {
2018                value = "force_on";
2019            } else if ((flags & StorageManager.DEBUG_SDCARDFS_FORCE_OFF) != 0) {
2020                value = "force_off";
2021            } else {
2022                value = "";
2023            }
2024
2025            final long token = Binder.clearCallingIdentity();
2026            try {
2027                SystemProperties.set(StorageManager.PROP_SDCARDFS, value);
2028
2029                // Reset storage to kick new setting into place
2030                mHandler.obtainMessage(H_RESET).sendToTarget();
2031            } finally {
2032                Binder.restoreCallingIdentity(token);
2033            }
2034        }
2035    }
2036
2037    @Override
2038    public String getPrimaryStorageUuid() {
2039        enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
2040        waitForReady();
2041
2042        synchronized (mLock) {
2043            return mPrimaryStorageUuid;
2044        }
2045    }
2046
2047    @Override
2048    public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) {
2049        enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
2050        waitForReady();
2051
2052        final VolumeInfo from;
2053        final VolumeInfo to;
2054
2055        synchronized (mLock) {
2056            if (Objects.equals(mPrimaryStorageUuid, volumeUuid)) {
2057                throw new IllegalArgumentException("Primary storage already at " + volumeUuid);
2058            }
2059
2060            if (mMoveCallback != null) {
2061                throw new IllegalStateException("Move already in progress");
2062            }
2063            mMoveCallback = callback;
2064            mMoveTargetUuid = volumeUuid;
2065
2066            // When moving to/from primary physical volume, we probably just nuked
2067            // the current storage location, so we have nothing to move.
2068            if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, mPrimaryStorageUuid)
2069                    || Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
2070                Slog.d(TAG, "Skipping move to/from primary physical");
2071                onMoveStatusLocked(MOVE_STATUS_COPY_FINISHED);
2072                onMoveStatusLocked(PackageManager.MOVE_SUCCEEDED);
2073                mHandler.obtainMessage(H_RESET).sendToTarget();
2074                return;
2075
2076            } else {
2077                from = findStorageForUuid(mPrimaryStorageUuid);
2078                to = findStorageForUuid(volumeUuid);
2079
2080                if (from == null) {
2081                    Slog.w(TAG, "Failing move due to missing from volume " + mPrimaryStorageUuid);
2082                    onMoveStatusLocked(PackageManager.MOVE_FAILED_INTERNAL_ERROR);
2083                    return;
2084                } else if (to == null) {
2085                    Slog.w(TAG, "Failing move due to missing to volume " + volumeUuid);
2086                    onMoveStatusLocked(PackageManager.MOVE_FAILED_INTERNAL_ERROR);
2087                    return;
2088                }
2089            }
2090        }
2091
2092        try {
2093            mConnector.execute("volume", "move_storage", from.id, to.id);
2094        } catch (NativeDaemonConnectorException e) {
2095            throw e.rethrowAsParcelableException();
2096        }
2097    }
2098
2099    @Override
2100    public int[] getStorageUsers(String path) {
2101        enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
2102        waitForReady();
2103        try {
2104            final String[] r = NativeDaemonEvent.filterMessageList(
2105                    mConnector.executeForList("storage", "users", path),
2106                    VoldResponseCode.StorageUsersListResult);
2107
2108            // FMT: <pid> <process name>
2109            int[] data = new int[r.length];
2110            for (int i = 0; i < r.length; i++) {
2111                String[] tok = r[i].split(" ");
2112                try {
2113                    data[i] = Integer.parseInt(tok[0]);
2114                } catch (NumberFormatException nfe) {
2115                    Slog.e(TAG, String.format("Error parsing pid %s", tok[0]));
2116                    return new int[0];
2117                }
2118            }
2119            return data;
2120        } catch (NativeDaemonConnectorException e) {
2121            Slog.e(TAG, "Failed to retrieve storage users list", e);
2122            return new int[0];
2123        }
2124    }
2125
2126    private void warnOnNotMounted() {
2127        synchronized (mLock) {
2128            for (int i = 0; i < mVolumes.size(); i++) {
2129                final VolumeInfo vol = mVolumes.valueAt(i);
2130                if (vol.isPrimary() && vol.isMountedWritable()) {
2131                    // Cool beans, we have a mounted primary volume
2132                    return;
2133                }
2134            }
2135        }
2136
2137        Slog.w(TAG, "No primary storage mounted!");
2138    }
2139
2140    public String[] getSecureContainerList() {
2141        enforcePermission(android.Manifest.permission.ASEC_ACCESS);
2142        waitForReady();
2143        warnOnNotMounted();
2144
2145        try {
2146            return NativeDaemonEvent.filterMessageList(
2147                    mConnector.executeForList("asec", "list"), VoldResponseCode.AsecListResult);
2148        } catch (NativeDaemonConnectorException e) {
2149            return new String[0];
2150        }
2151    }
2152
2153    public int createSecureContainer(String id, int sizeMb, String fstype, String key,
2154            int ownerUid, boolean external) {
2155        enforcePermission(android.Manifest.permission.ASEC_CREATE);
2156        waitForReady();
2157        warnOnNotMounted();
2158
2159        int rc = StorageResultCode.OperationSucceeded;
2160        try {
2161            mConnector.execute("asec", "create", id, sizeMb, fstype, new SensitiveArg(key),
2162                    ownerUid, external ? "1" : "0");
2163        } catch (NativeDaemonConnectorException e) {
2164            rc = StorageResultCode.OperationFailedInternalError;
2165        }
2166
2167        if (rc == StorageResultCode.OperationSucceeded) {
2168            synchronized (mAsecMountSet) {
2169                mAsecMountSet.add(id);
2170            }
2171        }
2172        return rc;
2173    }
2174
2175    @Override
2176    public int resizeSecureContainer(String id, int sizeMb, String key) {
2177        enforcePermission(android.Manifest.permission.ASEC_CREATE);
2178        waitForReady();
2179        warnOnNotMounted();
2180
2181        int rc = StorageResultCode.OperationSucceeded;
2182        try {
2183            mConnector.execute("asec", "resize", id, sizeMb, new SensitiveArg(key));
2184        } catch (NativeDaemonConnectorException e) {
2185            rc = StorageResultCode.OperationFailedInternalError;
2186        }
2187        return rc;
2188    }
2189
2190    public int finalizeSecureContainer(String id) {
2191        enforcePermission(android.Manifest.permission.ASEC_CREATE);
2192        warnOnNotMounted();
2193
2194        int rc = StorageResultCode.OperationSucceeded;
2195        try {
2196            mConnector.execute("asec", "finalize", id);
2197            /*
2198             * Finalization does a remount, so no need
2199             * to update mAsecMountSet
2200             */
2201        } catch (NativeDaemonConnectorException e) {
2202            rc = StorageResultCode.OperationFailedInternalError;
2203        }
2204        return rc;
2205    }
2206
2207    public int fixPermissionsSecureContainer(String id, int gid, String filename) {
2208        enforcePermission(android.Manifest.permission.ASEC_CREATE);
2209        warnOnNotMounted();
2210
2211        int rc = StorageResultCode.OperationSucceeded;
2212        try {
2213            mConnector.execute("asec", "fixperms", id, gid, filename);
2214            /*
2215             * Fix permissions does a remount, so no need to update
2216             * mAsecMountSet
2217             */
2218        } catch (NativeDaemonConnectorException e) {
2219            rc = StorageResultCode.OperationFailedInternalError;
2220        }
2221        return rc;
2222    }
2223
2224    public int destroySecureContainer(String id, boolean force) {
2225        enforcePermission(android.Manifest.permission.ASEC_DESTROY);
2226        waitForReady();
2227        warnOnNotMounted();
2228
2229        /*
2230         * Force a GC to make sure AssetManagers in other threads of the
2231         * system_server are cleaned up. We have to do this since AssetManager
2232         * instances are kept as a WeakReference and it's possible we have files
2233         * open on the external storage.
2234         */
2235        Runtime.getRuntime().gc();
2236
2237        int rc = StorageResultCode.OperationSucceeded;
2238        try {
2239            final Command cmd = new Command("asec", "destroy", id);
2240            if (force) {
2241                cmd.appendArg("force");
2242            }
2243            mConnector.execute(cmd);
2244        } catch (NativeDaemonConnectorException e) {
2245            int code = e.getCode();
2246            if (code == VoldResponseCode.OpFailedStorageBusy) {
2247                rc = StorageResultCode.OperationFailedStorageBusy;
2248            } else {
2249                rc = StorageResultCode.OperationFailedInternalError;
2250            }
2251        }
2252
2253        if (rc == StorageResultCode.OperationSucceeded) {
2254            synchronized (mAsecMountSet) {
2255                if (mAsecMountSet.contains(id)) {
2256                    mAsecMountSet.remove(id);
2257                }
2258            }
2259        }
2260
2261        return rc;
2262    }
2263
2264    public int mountSecureContainer(String id, String key, int ownerUid, boolean readOnly) {
2265        enforcePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
2266        waitForReady();
2267        warnOnNotMounted();
2268
2269        synchronized (mAsecMountSet) {
2270            if (mAsecMountSet.contains(id)) {
2271                return StorageResultCode.OperationFailedStorageMounted;
2272            }
2273        }
2274
2275        int rc = StorageResultCode.OperationSucceeded;
2276        try {
2277            mConnector.execute("asec", "mount", id, new SensitiveArg(key), ownerUid,
2278                    readOnly ? "ro" : "rw");
2279        } catch (NativeDaemonConnectorException e) {
2280            int code = e.getCode();
2281            if (code != VoldResponseCode.OpFailedStorageBusy) {
2282                rc = StorageResultCode.OperationFailedInternalError;
2283            }
2284        }
2285
2286        if (rc == StorageResultCode.OperationSucceeded) {
2287            synchronized (mAsecMountSet) {
2288                mAsecMountSet.add(id);
2289            }
2290        }
2291        return rc;
2292    }
2293
2294    public int unmountSecureContainer(String id, boolean force) {
2295        enforcePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
2296        waitForReady();
2297        warnOnNotMounted();
2298
2299        synchronized (mAsecMountSet) {
2300            if (!mAsecMountSet.contains(id)) {
2301                return StorageResultCode.OperationFailedStorageNotMounted;
2302            }
2303         }
2304
2305        /*
2306         * Force a GC to make sure AssetManagers in other threads of the
2307         * system_server are cleaned up. We have to do this since AssetManager
2308         * instances are kept as a WeakReference and it's possible we have files
2309         * open on the external storage.
2310         */
2311        Runtime.getRuntime().gc();
2312
2313        int rc = StorageResultCode.OperationSucceeded;
2314        try {
2315            final Command cmd = new Command("asec", "unmount", id);
2316            if (force) {
2317                cmd.appendArg("force");
2318            }
2319            mConnector.execute(cmd);
2320        } catch (NativeDaemonConnectorException e) {
2321            int code = e.getCode();
2322            if (code == VoldResponseCode.OpFailedStorageBusy) {
2323                rc = StorageResultCode.OperationFailedStorageBusy;
2324            } else {
2325                rc = StorageResultCode.OperationFailedInternalError;
2326            }
2327        }
2328
2329        if (rc == StorageResultCode.OperationSucceeded) {
2330            synchronized (mAsecMountSet) {
2331                mAsecMountSet.remove(id);
2332            }
2333        }
2334        return rc;
2335    }
2336
2337    public boolean isSecureContainerMounted(String id) {
2338        enforcePermission(android.Manifest.permission.ASEC_ACCESS);
2339        waitForReady();
2340        warnOnNotMounted();
2341
2342        synchronized (mAsecMountSet) {
2343            return mAsecMountSet.contains(id);
2344        }
2345    }
2346
2347    public int renameSecureContainer(String oldId, String newId) {
2348        enforcePermission(android.Manifest.permission.ASEC_RENAME);
2349        waitForReady();
2350        warnOnNotMounted();
2351
2352        synchronized (mAsecMountSet) {
2353            /*
2354             * Because a mounted container has active internal state which cannot be
2355             * changed while active, we must ensure both ids are not currently mounted.
2356             */
2357            if (mAsecMountSet.contains(oldId) || mAsecMountSet.contains(newId)) {
2358                return StorageResultCode.OperationFailedStorageMounted;
2359            }
2360        }
2361
2362        int rc = StorageResultCode.OperationSucceeded;
2363        try {
2364            mConnector.execute("asec", "rename", oldId, newId);
2365        } catch (NativeDaemonConnectorException e) {
2366            rc = StorageResultCode.OperationFailedInternalError;
2367        }
2368
2369        return rc;
2370    }
2371
2372    public String getSecureContainerPath(String id) {
2373        enforcePermission(android.Manifest.permission.ASEC_ACCESS);
2374        waitForReady();
2375        warnOnNotMounted();
2376
2377        final NativeDaemonEvent event;
2378        try {
2379            event = mConnector.execute("asec", "path", id);
2380            event.checkCode(VoldResponseCode.AsecPathResult);
2381            return event.getMessage();
2382        } catch (NativeDaemonConnectorException e) {
2383            int code = e.getCode();
2384            if (code == VoldResponseCode.OpFailedStorageNotFound) {
2385                Slog.i(TAG, String.format("Container '%s' not found", id));
2386                return null;
2387            } else {
2388                throw new IllegalStateException(String.format("Unexpected response code %d", code));
2389            }
2390        }
2391    }
2392
2393    public String getSecureContainerFilesystemPath(String id) {
2394        enforcePermission(android.Manifest.permission.ASEC_ACCESS);
2395        waitForReady();
2396        warnOnNotMounted();
2397
2398        final NativeDaemonEvent event;
2399        try {
2400            event = mConnector.execute("asec", "fspath", id);
2401            event.checkCode(VoldResponseCode.AsecPathResult);
2402            return event.getMessage();
2403        } catch (NativeDaemonConnectorException e) {
2404            int code = e.getCode();
2405            if (code == VoldResponseCode.OpFailedStorageNotFound) {
2406                Slog.i(TAG, String.format("Container '%s' not found", id));
2407                return null;
2408            } else {
2409                throw new IllegalStateException(String.format("Unexpected response code %d", code));
2410            }
2411        }
2412    }
2413
2414    @Override
2415    public void finishMediaUpdate() {
2416        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
2417            throw new SecurityException("no permission to call finishMediaUpdate()");
2418        }
2419        if (mUnmountSignal != null) {
2420            mUnmountSignal.countDown();
2421        } else {
2422            Slog.w(TAG, "Odd, nobody asked to unmount?");
2423        }
2424    }
2425
2426    private boolean isUidOwnerOfPackageOrSystem(String packageName, int callerUid) {
2427        if (callerUid == android.os.Process.SYSTEM_UID) {
2428            return true;
2429        }
2430
2431        if (packageName == null) {
2432            return false;
2433        }
2434
2435        final int packageUid = mPms.getPackageUid(packageName,
2436                PackageManager.MATCH_DEBUG_TRIAGED_MISSING, UserHandle.getUserId(callerUid));
2437
2438        if (DEBUG_OBB) {
2439            Slog.d(TAG, "packageName = " + packageName + ", packageUid = " +
2440                    packageUid + ", callerUid = " + callerUid);
2441        }
2442
2443        return callerUid == packageUid;
2444    }
2445
2446    public String getMountedObbPath(String rawPath) {
2447        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
2448
2449        waitForReady();
2450        warnOnNotMounted();
2451
2452        final ObbState state;
2453        synchronized (mObbMounts) {
2454            state = mObbPathToStateMap.get(rawPath);
2455        }
2456        if (state == null) {
2457            Slog.w(TAG, "Failed to find OBB mounted at " + rawPath);
2458            return null;
2459        }
2460
2461        final NativeDaemonEvent event;
2462        try {
2463            event = mConnector.execute("obb", "path", state.canonicalPath);
2464            event.checkCode(VoldResponseCode.AsecPathResult);
2465            return event.getMessage();
2466        } catch (NativeDaemonConnectorException e) {
2467            int code = e.getCode();
2468            if (code == VoldResponseCode.OpFailedStorageNotFound) {
2469                return null;
2470            } else {
2471                throw new IllegalStateException(String.format("Unexpected response code %d", code));
2472            }
2473        }
2474    }
2475
2476    @Override
2477    public boolean isObbMounted(String rawPath) {
2478        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
2479        synchronized (mObbMounts) {
2480            return mObbPathToStateMap.containsKey(rawPath);
2481        }
2482    }
2483
2484    @Override
2485    public void mountObb(
2486            String rawPath, String canonicalPath, String key, IObbActionListener token, int nonce) {
2487        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
2488        Preconditions.checkNotNull(canonicalPath, "canonicalPath cannot be null");
2489        Preconditions.checkNotNull(token, "token cannot be null");
2490
2491        final int callingUid = Binder.getCallingUid();
2492        final ObbState obbState = new ObbState(rawPath, canonicalPath, callingUid, token, nonce);
2493        final ObbAction action = new MountObbAction(obbState, key, callingUid);
2494        mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
2495
2496        if (DEBUG_OBB)
2497            Slog.i(TAG, "Send to OBB handler: " + action.toString());
2498    }
2499
2500    @Override
2501    public void unmountObb(String rawPath, boolean force, IObbActionListener token, int nonce) {
2502        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
2503
2504        final ObbState existingState;
2505        synchronized (mObbMounts) {
2506            existingState = mObbPathToStateMap.get(rawPath);
2507        }
2508
2509        if (existingState != null) {
2510            // TODO: separate state object from request data
2511            final int callingUid = Binder.getCallingUid();
2512            final ObbState newState = new ObbState(
2513                    rawPath, existingState.canonicalPath, callingUid, token, nonce);
2514            final ObbAction action = new UnmountObbAction(newState, force);
2515            mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
2516
2517            if (DEBUG_OBB)
2518                Slog.i(TAG, "Send to OBB handler: " + action.toString());
2519        } else {
2520            Slog.w(TAG, "Unknown OBB mount at " + rawPath);
2521        }
2522    }
2523
2524    @Override
2525    public int getEncryptionState() {
2526        mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2527                "no permission to access the crypt keeper");
2528
2529        waitForReady();
2530
2531        final NativeDaemonEvent event;
2532        try {
2533            event = mCryptConnector.execute("cryptfs", "cryptocomplete");
2534            return Integer.parseInt(event.getMessage());
2535        } catch (NumberFormatException e) {
2536            // Bad result - unexpected.
2537            Slog.w(TAG, "Unable to parse result from cryptfs cryptocomplete");
2538            return ENCRYPTION_STATE_ERROR_UNKNOWN;
2539        } catch (NativeDaemonConnectorException e) {
2540            // Something bad happened.
2541            Slog.w(TAG, "Error in communicating with cryptfs in validating");
2542            return ENCRYPTION_STATE_ERROR_UNKNOWN;
2543        }
2544    }
2545
2546    @Override
2547    public int decryptStorage(String password) {
2548        if (TextUtils.isEmpty(password)) {
2549            throw new IllegalArgumentException("password cannot be empty");
2550        }
2551
2552        mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2553                "no permission to access the crypt keeper");
2554
2555        waitForReady();
2556
2557        if (DEBUG_EVENTS) {
2558            Slog.i(TAG, "decrypting storage...");
2559        }
2560
2561        final NativeDaemonEvent event;
2562        try {
2563            event = mCryptConnector.execute("cryptfs", "checkpw", new SensitiveArg(password));
2564
2565            final int code = Integer.parseInt(event.getMessage());
2566            if (code == 0) {
2567                // Decrypt was successful. Post a delayed message before restarting in order
2568                // to let the UI to clear itself
2569                mHandler.postDelayed(new Runnable() {
2570                    public void run() {
2571                        try {
2572                            mCryptConnector.execute("cryptfs", "restart");
2573                        } catch (NativeDaemonConnectorException e) {
2574                            Slog.e(TAG, "problem executing in background", e);
2575                        }
2576                    }
2577                }, 1000); // 1 second
2578            }
2579
2580            return code;
2581        } catch (NativeDaemonConnectorException e) {
2582            // Decryption failed
2583            return e.getCode();
2584        }
2585    }
2586
2587    public int encryptStorage(int type, String password) {
2588        if (TextUtils.isEmpty(password) && type != StorageManager.CRYPT_TYPE_DEFAULT) {
2589            throw new IllegalArgumentException("password cannot be empty");
2590        }
2591
2592        mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2593            "no permission to access the crypt keeper");
2594
2595        waitForReady();
2596
2597        if (DEBUG_EVENTS) {
2598            Slog.i(TAG, "encrypting storage...");
2599        }
2600
2601        try {
2602            if (type == StorageManager.CRYPT_TYPE_DEFAULT) {
2603                mCryptConnector.execute("cryptfs", "enablecrypto", "inplace",
2604                                CRYPTO_TYPES[type]);
2605            } else {
2606                mCryptConnector.execute("cryptfs", "enablecrypto", "inplace",
2607                                CRYPTO_TYPES[type], new SensitiveArg(password));
2608            }
2609        } catch (NativeDaemonConnectorException e) {
2610            // Encryption failed
2611            return e.getCode();
2612        }
2613
2614        return 0;
2615    }
2616
2617    /** Set the password for encrypting the master key.
2618     *  @param type One of the CRYPTO_TYPE_XXX consts defined in StorageManager.
2619     *  @param password The password to set.
2620     */
2621    public int changeEncryptionPassword(int type, String password) {
2622        mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2623            "no permission to access the crypt keeper");
2624
2625        waitForReady();
2626
2627        if (DEBUG_EVENTS) {
2628            Slog.i(TAG, "changing encryption password...");
2629        }
2630
2631        try {
2632            NativeDaemonEvent event = mCryptConnector.execute("cryptfs", "changepw", CRYPTO_TYPES[type],
2633                        new SensitiveArg(password));
2634            return Integer.parseInt(event.getMessage());
2635        } catch (NativeDaemonConnectorException e) {
2636            // Encryption failed
2637            return e.getCode();
2638        }
2639    }
2640
2641    /**
2642     * Validate a user-supplied password string with cryptfs
2643     */
2644    @Override
2645    public int verifyEncryptionPassword(String password) throws RemoteException {
2646        // Only the system process is permitted to validate passwords
2647        if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
2648            throw new SecurityException("no permission to access the crypt keeper");
2649        }
2650
2651        mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2652            "no permission to access the crypt keeper");
2653
2654        if (TextUtils.isEmpty(password)) {
2655            throw new IllegalArgumentException("password cannot be empty");
2656        }
2657
2658        waitForReady();
2659
2660        if (DEBUG_EVENTS) {
2661            Slog.i(TAG, "validating encryption password...");
2662        }
2663
2664        final NativeDaemonEvent event;
2665        try {
2666            event = mCryptConnector.execute("cryptfs", "verifypw", new SensitiveArg(password));
2667            Slog.i(TAG, "cryptfs verifypw => " + event.getMessage());
2668            return Integer.parseInt(event.getMessage());
2669        } catch (NativeDaemonConnectorException e) {
2670            // Encryption failed
2671            return e.getCode();
2672        }
2673    }
2674
2675    /**
2676     * Get the type of encryption used to encrypt the master key.
2677     * @return The type, one of the CRYPT_TYPE_XXX consts from StorageManager.
2678     */
2679    @Override
2680    public int getPasswordType() {
2681        mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL,
2682            "no permission to access the crypt keeper");
2683
2684        waitForReady();
2685
2686        final NativeDaemonEvent event;
2687        try {
2688            event = mCryptConnector.execute("cryptfs", "getpwtype");
2689            for (int i = 0; i < CRYPTO_TYPES.length; ++i) {
2690                if (CRYPTO_TYPES[i].equals(event.getMessage()))
2691                    return i;
2692            }
2693
2694            throw new IllegalStateException("unexpected return from cryptfs");
2695        } catch (NativeDaemonConnectorException e) {
2696            throw e.rethrowAsParcelableException();
2697        }
2698    }
2699
2700    /**
2701     * Set a field in the crypto header.
2702     * @param field field to set
2703     * @param contents contents to set in field
2704     */
2705    @Override
2706    public void setField(String field, String contents) throws RemoteException {
2707        mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL,
2708            "no permission to access the crypt keeper");
2709
2710        waitForReady();
2711
2712        final NativeDaemonEvent event;
2713        try {
2714            event = mCryptConnector.execute("cryptfs", "setfield", field, contents);
2715        } catch (NativeDaemonConnectorException e) {
2716            throw e.rethrowAsParcelableException();
2717        }
2718    }
2719
2720    /**
2721     * Gets a field from the crypto header.
2722     * @param field field to get
2723     * @return contents of field
2724     */
2725    @Override
2726    public String getField(String field) throws RemoteException {
2727        mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL,
2728            "no permission to access the crypt keeper");
2729
2730        waitForReady();
2731
2732        final NativeDaemonEvent event;
2733        try {
2734            final String[] contents = NativeDaemonEvent.filterMessageList(
2735                    mCryptConnector.executeForList("cryptfs", "getfield", field),
2736                    VoldResponseCode.CryptfsGetfieldResult);
2737            String result = new String();
2738            for (String content : contents) {
2739                result += content;
2740            }
2741            return result;
2742        } catch (NativeDaemonConnectorException e) {
2743            throw e.rethrowAsParcelableException();
2744        }
2745    }
2746
2747    /**
2748     * Is userdata convertible to file based encryption?
2749     * @return non zero for convertible
2750     */
2751    @Override
2752    public boolean isConvertibleToFBE() throws RemoteException {
2753        mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL,
2754            "no permission to access the crypt keeper");
2755
2756        waitForReady();
2757
2758        final NativeDaemonEvent event;
2759        try {
2760            event = mCryptConnector.execute("cryptfs", "isConvertibleToFBE");
2761            return Integer.parseInt(event.getMessage()) != 0;
2762        } catch (NativeDaemonConnectorException e) {
2763            throw e.rethrowAsParcelableException();
2764        }
2765    }
2766
2767    @Override
2768    public String getPassword() throws RemoteException {
2769        mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL,
2770                "only keyguard can retrieve password");
2771
2772        if (!isReady()) {
2773            return new String();
2774        }
2775
2776        final NativeDaemonEvent event;
2777        try {
2778            event = mCryptConnector.execute("cryptfs", "getpw");
2779            if ("-1".equals(event.getMessage())) {
2780                // -1 equals no password
2781                return null;
2782            }
2783            return event.getMessage();
2784        } catch (NativeDaemonConnectorException e) {
2785            throw e.rethrowAsParcelableException();
2786        } catch (IllegalArgumentException e) {
2787            Slog.e(TAG, "Invalid response to getPassword");
2788            return null;
2789        }
2790    }
2791
2792    @Override
2793    public void clearPassword() throws RemoteException {
2794        mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL,
2795                "only keyguard can clear password");
2796
2797        if (!isReady()) {
2798            return;
2799        }
2800
2801        final NativeDaemonEvent event;
2802        try {
2803            event = mCryptConnector.execute("cryptfs", "clearpw");
2804        } catch (NativeDaemonConnectorException e) {
2805            throw e.rethrowAsParcelableException();
2806        }
2807    }
2808
2809    @Override
2810    public void createUserKey(int userId, int serialNumber, boolean ephemeral) {
2811        enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
2812        waitForReady();
2813
2814        try {
2815            mCryptConnector.execute("cryptfs", "create_user_key", userId, serialNumber,
2816                ephemeral ? 1 : 0);
2817        } catch (NativeDaemonConnectorException e) {
2818            throw e.rethrowAsParcelableException();
2819        }
2820    }
2821
2822    @Override
2823    public void destroyUserKey(int userId) {
2824        enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
2825        waitForReady();
2826
2827        try {
2828            mCryptConnector.execute("cryptfs", "destroy_user_key", userId);
2829        } catch (NativeDaemonConnectorException e) {
2830            throw e.rethrowAsParcelableException();
2831        }
2832    }
2833
2834    private SensitiveArg encodeBytes(byte[] bytes) {
2835        if (ArrayUtils.isEmpty(bytes)) {
2836            return new SensitiveArg("!");
2837        } else {
2838            return new SensitiveArg(HexDump.toHexString(bytes));
2839        }
2840    }
2841
2842    /*
2843     * Add this token/secret pair to the set of ways we can recover a disk encryption key.
2844     * Changing the token/secret for a disk encryption key is done in two phases: first, adding
2845     * a new token/secret pair with this call, then delting all other pairs with
2846     * fixateNewestUserKeyAuth. This allows other places where a credential is used, such as
2847     * Gatekeeper, to be updated between the two calls.
2848     */
2849    @Override
2850    public void addUserKeyAuth(int userId, int serialNumber, byte[] token, byte[] secret) {
2851        enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
2852        waitForReady();
2853
2854        try {
2855            mCryptConnector.execute("cryptfs", "add_user_key_auth", userId, serialNumber,
2856                encodeBytes(token), encodeBytes(secret));
2857        } catch (NativeDaemonConnectorException e) {
2858            throw e.rethrowAsParcelableException();
2859        }
2860    }
2861
2862    /*
2863     * Delete all disk encryption token/secret pairs except the most recently added one
2864     */
2865    @Override
2866    public void fixateNewestUserKeyAuth(int userId) {
2867        enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
2868        waitForReady();
2869
2870        try {
2871            mCryptConnector.execute("cryptfs", "fixate_newest_user_key_auth", userId);
2872        } catch (NativeDaemonConnectorException e) {
2873            throw e.rethrowAsParcelableException();
2874        }
2875    }
2876
2877    @Override
2878    public void unlockUserKey(int userId, int serialNumber, byte[] token, byte[] secret) {
2879        enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
2880        waitForReady();
2881
2882        if (StorageManager.isFileEncryptedNativeOrEmulated()) {
2883            // When a user has secure lock screen, require a challenge token to
2884            // actually unlock. This check is mostly in place for emulation mode.
2885            if (mLockPatternUtils.isSecure(userId) && ArrayUtils.isEmpty(token)) {
2886                throw new IllegalStateException("Token required to unlock secure user " + userId);
2887            }
2888
2889            try {
2890                mCryptConnector.execute("cryptfs", "unlock_user_key", userId, serialNumber,
2891                        encodeBytes(token), encodeBytes(secret));
2892            } catch (NativeDaemonConnectorException e) {
2893                throw e.rethrowAsParcelableException();
2894            }
2895        }
2896
2897        synchronized (mLock) {
2898            mLocalUnlockedUsers = ArrayUtils.appendInt(mLocalUnlockedUsers, userId);
2899        }
2900    }
2901
2902    @Override
2903    public void lockUserKey(int userId) {
2904        enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
2905        waitForReady();
2906
2907        try {
2908            mCryptConnector.execute("cryptfs", "lock_user_key", userId);
2909        } catch (NativeDaemonConnectorException e) {
2910            throw e.rethrowAsParcelableException();
2911        }
2912
2913        synchronized (mLock) {
2914            mLocalUnlockedUsers = ArrayUtils.removeInt(mLocalUnlockedUsers, userId);
2915        }
2916    }
2917
2918    @Override
2919    public boolean isUserKeyUnlocked(int userId) {
2920        synchronized (mLock) {
2921            return ArrayUtils.contains(mLocalUnlockedUsers, userId);
2922        }
2923    }
2924
2925    @Override
2926    public void prepareUserStorage(String volumeUuid, int userId, int serialNumber, int flags) {
2927        enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
2928        waitForReady();
2929
2930        try {
2931            mCryptConnector.execute("cryptfs", "prepare_user_storage", escapeNull(volumeUuid),
2932                    userId, serialNumber, flags);
2933        } catch (NativeDaemonConnectorException e) {
2934            throw e.rethrowAsParcelableException();
2935        }
2936    }
2937
2938    @Override
2939    public void destroyUserStorage(String volumeUuid, int userId, int flags) {
2940        enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
2941        waitForReady();
2942
2943        try {
2944            mCryptConnector.execute("cryptfs", "destroy_user_storage", escapeNull(volumeUuid),
2945                    userId, flags);
2946        } catch (NativeDaemonConnectorException e) {
2947            throw e.rethrowAsParcelableException();
2948        }
2949    }
2950
2951    @Override
2952    public ParcelFileDescriptor mountAppFuse(final String name) throws RemoteException {
2953        try {
2954            final int uid = Binder.getCallingUid();
2955            final int pid = Binder.getCallingPid();
2956            final NativeDaemonEvent event =
2957                    mConnector.execute("appfuse", "mount", uid, pid, name);
2958            if (event.getFileDescriptors() == null) {
2959                throw new RemoteException("AppFuse FD from vold is null.");
2960            }
2961            return ParcelFileDescriptor.fromFd(
2962                    event.getFileDescriptors()[0],
2963                    mHandler,
2964                    new ParcelFileDescriptor.OnCloseListener() {
2965                        @Override
2966                        public void onClose(IOException e) {
2967                            try {
2968                                final NativeDaemonEvent event = mConnector.execute(
2969                                        "appfuse", "unmount", uid, pid, name);
2970                            } catch (NativeDaemonConnectorException unmountException) {
2971                                Log.e(TAG, "Failed to unmount appfuse.");
2972                            }
2973                        }
2974                    });
2975        } catch (NativeDaemonConnectorException e) {
2976            throw e.rethrowAsParcelableException();
2977        } catch (IOException e) {
2978            throw new RemoteException(e.getMessage());
2979        }
2980    }
2981
2982    @Override
2983    public int mkdirs(String callingPkg, String appPath) {
2984        final int userId = UserHandle.getUserId(Binder.getCallingUid());
2985        final UserEnvironment userEnv = new UserEnvironment(userId);
2986
2987        // Validate that reported package name belongs to caller
2988        final AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(
2989                Context.APP_OPS_SERVICE);
2990        appOps.checkPackage(Binder.getCallingUid(), callingPkg);
2991
2992        File appFile = null;
2993        try {
2994            appFile = new File(appPath).getCanonicalFile();
2995        } catch (IOException e) {
2996            Slog.e(TAG, "Failed to resolve " + appPath + ": " + e);
2997            return -1;
2998        }
2999
3000        // Try translating the app path into a vold path, but require that it
3001        // belong to the calling package.
3002        if (FileUtils.contains(userEnv.buildExternalStorageAppDataDirs(callingPkg), appFile) ||
3003                FileUtils.contains(userEnv.buildExternalStorageAppObbDirs(callingPkg), appFile) ||
3004                FileUtils.contains(userEnv.buildExternalStorageAppMediaDirs(callingPkg), appFile)) {
3005            appPath = appFile.getAbsolutePath();
3006            if (!appPath.endsWith("/")) {
3007                appPath = appPath + "/";
3008            }
3009
3010            try {
3011                mConnector.execute("volume", "mkdirs", appPath);
3012                return 0;
3013            } catch (NativeDaemonConnectorException e) {
3014                return e.getCode();
3015            }
3016        }
3017
3018        throw new SecurityException("Invalid mkdirs path: " + appFile);
3019    }
3020
3021    @Override
3022    public StorageVolume[] getVolumeList(int uid, String packageName, int flags) {
3023        final int userId = UserHandle.getUserId(uid);
3024
3025        final boolean forWrite = (flags & StorageManager.FLAG_FOR_WRITE) != 0;
3026        final boolean realState = (flags & StorageManager.FLAG_REAL_STATE) != 0;
3027        final boolean includeInvisible = (flags & StorageManager.FLAG_INCLUDE_INVISIBLE) != 0;
3028
3029        final boolean userKeyUnlocked;
3030        final boolean storagePermission;
3031        final long token = Binder.clearCallingIdentity();
3032        try {
3033            userKeyUnlocked = isUserKeyUnlocked(userId);
3034            storagePermission = mMountServiceInternal.hasExternalStorage(uid, packageName);
3035        } finally {
3036            Binder.restoreCallingIdentity(token);
3037        }
3038
3039        boolean foundPrimary = false;
3040
3041        final ArrayList<StorageVolume> res = new ArrayList<>();
3042        synchronized (mLock) {
3043            for (int i = 0; i < mVolumes.size(); i++) {
3044                final VolumeInfo vol = mVolumes.valueAt(i);
3045                switch (vol.getType()) {
3046                    case VolumeInfo.TYPE_PUBLIC:
3047                    case VolumeInfo.TYPE_EMULATED:
3048                        break;
3049                    default:
3050                        continue;
3051                }
3052
3053                boolean match = false;
3054                if (forWrite) {
3055                    match = vol.isVisibleForWrite(userId);
3056                } else {
3057                    match = vol.isVisibleForRead(userId)
3058                            || (includeInvisible && vol.getPath() != null);
3059                }
3060                if (!match) continue;
3061
3062                boolean reportUnmounted = false;
3063                if ((vol.getType() == VolumeInfo.TYPE_EMULATED) && !userKeyUnlocked) {
3064                    reportUnmounted = true;
3065                } else if (!storagePermission && !realState) {
3066                    reportUnmounted = true;
3067                }
3068
3069                final StorageVolume userVol = vol.buildStorageVolume(mContext, userId,
3070                        reportUnmounted);
3071                if (vol.isPrimary()) {
3072                    res.add(0, userVol);
3073                    foundPrimary = true;
3074                } else {
3075                    res.add(userVol);
3076                }
3077            }
3078        }
3079
3080        if (!foundPrimary) {
3081            Log.w(TAG, "No primary storage defined yet; hacking together a stub");
3082
3083            final boolean primaryPhysical = SystemProperties.getBoolean(
3084                    StorageManager.PROP_PRIMARY_PHYSICAL, false);
3085
3086            final String id = "stub_primary";
3087            final File path = Environment.getLegacyExternalStorageDirectory();
3088            final String description = mContext.getString(android.R.string.unknownName);
3089            final boolean primary = true;
3090            final boolean removable = primaryPhysical;
3091            final boolean emulated = !primaryPhysical;
3092            final long mtpReserveSize = 0L;
3093            final boolean allowMassStorage = false;
3094            final long maxFileSize = 0L;
3095            final UserHandle owner = new UserHandle(userId);
3096            final String uuid = null;
3097            final String state = Environment.MEDIA_REMOVED;
3098
3099            res.add(0, new StorageVolume(id, StorageVolume.STORAGE_ID_INVALID, path,
3100                    description, primary, removable, emulated, mtpReserveSize,
3101                    allowMassStorage, maxFileSize, owner, uuid, state));
3102        }
3103
3104        return res.toArray(new StorageVolume[res.size()]);
3105    }
3106
3107    @Override
3108    public DiskInfo[] getDisks() {
3109        synchronized (mLock) {
3110            final DiskInfo[] res = new DiskInfo[mDisks.size()];
3111            for (int i = 0; i < mDisks.size(); i++) {
3112                res[i] = mDisks.valueAt(i);
3113            }
3114            return res;
3115        }
3116    }
3117
3118    @Override
3119    public VolumeInfo[] getVolumes(int flags) {
3120        synchronized (mLock) {
3121            final VolumeInfo[] res = new VolumeInfo[mVolumes.size()];
3122            for (int i = 0; i < mVolumes.size(); i++) {
3123                res[i] = mVolumes.valueAt(i);
3124            }
3125            return res;
3126        }
3127    }
3128
3129    @Override
3130    public VolumeRecord[] getVolumeRecords(int flags) {
3131        synchronized (mLock) {
3132            final VolumeRecord[] res = new VolumeRecord[mRecords.size()];
3133            for (int i = 0; i < mRecords.size(); i++) {
3134                res[i] = mRecords.valueAt(i);
3135            }
3136            return res;
3137        }
3138    }
3139
3140    private void addObbStateLocked(ObbState obbState) throws RemoteException {
3141        final IBinder binder = obbState.getBinder();
3142        List<ObbState> obbStates = mObbMounts.get(binder);
3143
3144        if (obbStates == null) {
3145            obbStates = new ArrayList<ObbState>();
3146            mObbMounts.put(binder, obbStates);
3147        } else {
3148            for (final ObbState o : obbStates) {
3149                if (o.rawPath.equals(obbState.rawPath)) {
3150                    throw new IllegalStateException("Attempt to add ObbState twice. "
3151                            + "This indicates an error in the MountService logic.");
3152                }
3153            }
3154        }
3155
3156        obbStates.add(obbState);
3157        try {
3158            obbState.link();
3159        } catch (RemoteException e) {
3160            /*
3161             * The binder died before we could link it, so clean up our state
3162             * and return failure.
3163             */
3164            obbStates.remove(obbState);
3165            if (obbStates.isEmpty()) {
3166                mObbMounts.remove(binder);
3167            }
3168
3169            // Rethrow the error so mountObb can get it
3170            throw e;
3171        }
3172
3173        mObbPathToStateMap.put(obbState.rawPath, obbState);
3174    }
3175
3176    private void removeObbStateLocked(ObbState obbState) {
3177        final IBinder binder = obbState.getBinder();
3178        final List<ObbState> obbStates = mObbMounts.get(binder);
3179        if (obbStates != null) {
3180            if (obbStates.remove(obbState)) {
3181                obbState.unlink();
3182            }
3183            if (obbStates.isEmpty()) {
3184                mObbMounts.remove(binder);
3185            }
3186        }
3187
3188        mObbPathToStateMap.remove(obbState.rawPath);
3189    }
3190
3191    private class ObbActionHandler extends Handler {
3192        private boolean mBound = false;
3193        private final List<ObbAction> mActions = new LinkedList<ObbAction>();
3194
3195        ObbActionHandler(Looper l) {
3196            super(l);
3197        }
3198
3199        @Override
3200        public void handleMessage(Message msg) {
3201            switch (msg.what) {
3202                case OBB_RUN_ACTION: {
3203                    final ObbAction action = (ObbAction) msg.obj;
3204
3205                    if (DEBUG_OBB)
3206                        Slog.i(TAG, "OBB_RUN_ACTION: " + action.toString());
3207
3208                    // If a bind was already initiated we don't really
3209                    // need to do anything. The pending install
3210                    // will be processed later on.
3211                    if (!mBound) {
3212                        // If this is the only one pending we might
3213                        // have to bind to the service again.
3214                        if (!connectToService()) {
3215                            Slog.e(TAG, "Failed to bind to media container service");
3216                            action.handleError();
3217                            return;
3218                        }
3219                    }
3220
3221                    mActions.add(action);
3222                    break;
3223                }
3224                case OBB_MCS_BOUND: {
3225                    if (DEBUG_OBB)
3226                        Slog.i(TAG, "OBB_MCS_BOUND");
3227                    if (msg.obj != null) {
3228                        mContainerService = (IMediaContainerService) msg.obj;
3229                    }
3230                    if (mContainerService == null) {
3231                        // Something seriously wrong. Bail out
3232                        Slog.e(TAG, "Cannot bind to media container service");
3233                        for (ObbAction action : mActions) {
3234                            // Indicate service bind error
3235                            action.handleError();
3236                        }
3237                        mActions.clear();
3238                    } else if (mActions.size() > 0) {
3239                        final ObbAction action = mActions.get(0);
3240                        if (action != null) {
3241                            action.execute(this);
3242                        }
3243                    } else {
3244                        // Should never happen ideally.
3245                        Slog.w(TAG, "Empty queue");
3246                    }
3247                    break;
3248                }
3249                case OBB_MCS_RECONNECT: {
3250                    if (DEBUG_OBB)
3251                        Slog.i(TAG, "OBB_MCS_RECONNECT");
3252                    if (mActions.size() > 0) {
3253                        if (mBound) {
3254                            disconnectService();
3255                        }
3256                        if (!connectToService()) {
3257                            Slog.e(TAG, "Failed to bind to media container service");
3258                            for (ObbAction action : mActions) {
3259                                // Indicate service bind error
3260                                action.handleError();
3261                            }
3262                            mActions.clear();
3263                        }
3264                    }
3265                    break;
3266                }
3267                case OBB_MCS_UNBIND: {
3268                    if (DEBUG_OBB)
3269                        Slog.i(TAG, "OBB_MCS_UNBIND");
3270
3271                    // Delete pending install
3272                    if (mActions.size() > 0) {
3273                        mActions.remove(0);
3274                    }
3275                    if (mActions.size() == 0) {
3276                        if (mBound) {
3277                            disconnectService();
3278                        }
3279                    } else {
3280                        // There are more pending requests in queue.
3281                        // Just post MCS_BOUND message to trigger processing
3282                        // of next pending install.
3283                        mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND);
3284                    }
3285                    break;
3286                }
3287                case OBB_FLUSH_MOUNT_STATE: {
3288                    final String path = (String) msg.obj;
3289
3290                    if (DEBUG_OBB)
3291                        Slog.i(TAG, "Flushing all OBB state for path " + path);
3292
3293                    synchronized (mObbMounts) {
3294                        final List<ObbState> obbStatesToRemove = new LinkedList<ObbState>();
3295
3296                        final Iterator<ObbState> i = mObbPathToStateMap.values().iterator();
3297                        while (i.hasNext()) {
3298                            final ObbState state = i.next();
3299
3300                            /*
3301                             * If this entry's source file is in the volume path
3302                             * that got unmounted, remove it because it's no
3303                             * longer valid.
3304                             */
3305                            if (state.canonicalPath.startsWith(path)) {
3306                                obbStatesToRemove.add(state);
3307                            }
3308                        }
3309
3310                        for (final ObbState obbState : obbStatesToRemove) {
3311                            if (DEBUG_OBB)
3312                                Slog.i(TAG, "Removing state for " + obbState.rawPath);
3313
3314                            removeObbStateLocked(obbState);
3315
3316                            try {
3317                                obbState.token.onObbResult(obbState.rawPath, obbState.nonce,
3318                                        OnObbStateChangeListener.UNMOUNTED);
3319                            } catch (RemoteException e) {
3320                                Slog.i(TAG, "Couldn't send unmount notification for  OBB: "
3321                                        + obbState.rawPath);
3322                            }
3323                        }
3324                    }
3325                    break;
3326                }
3327            }
3328        }
3329
3330        private boolean connectToService() {
3331            if (DEBUG_OBB)
3332                Slog.i(TAG, "Trying to bind to DefaultContainerService");
3333
3334            Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
3335            if (mContext.bindServiceAsUser(service, mDefContainerConn, Context.BIND_AUTO_CREATE,
3336                    UserHandle.SYSTEM)) {
3337                mBound = true;
3338                return true;
3339            }
3340            return false;
3341        }
3342
3343        private void disconnectService() {
3344            mContainerService = null;
3345            mBound = false;
3346            mContext.unbindService(mDefContainerConn);
3347        }
3348    }
3349
3350    abstract class ObbAction {
3351        private static final int MAX_RETRIES = 3;
3352        private int mRetries;
3353
3354        ObbState mObbState;
3355
3356        ObbAction(ObbState obbState) {
3357            mObbState = obbState;
3358        }
3359
3360        public void execute(ObbActionHandler handler) {
3361            try {
3362                if (DEBUG_OBB)
3363                    Slog.i(TAG, "Starting to execute action: " + toString());
3364                mRetries++;
3365                if (mRetries > MAX_RETRIES) {
3366                    Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
3367                    mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
3368                    handleError();
3369                } else {
3370                    handleExecute();
3371                    if (DEBUG_OBB)
3372                        Slog.i(TAG, "Posting install MCS_UNBIND");
3373                    mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
3374                }
3375            } catch (RemoteException e) {
3376                if (DEBUG_OBB)
3377                    Slog.i(TAG, "Posting install MCS_RECONNECT");
3378                mObbActionHandler.sendEmptyMessage(OBB_MCS_RECONNECT);
3379            } catch (Exception e) {
3380                if (DEBUG_OBB)
3381                    Slog.d(TAG, "Error handling OBB action", e);
3382                handleError();
3383                mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
3384            }
3385        }
3386
3387        abstract void handleExecute() throws RemoteException, IOException;
3388        abstract void handleError();
3389
3390        protected ObbInfo getObbInfo() throws IOException {
3391            ObbInfo obbInfo;
3392            try {
3393                obbInfo = mContainerService.getObbInfo(mObbState.canonicalPath);
3394            } catch (RemoteException e) {
3395                Slog.d(TAG, "Couldn't call DefaultContainerService to fetch OBB info for "
3396                        + mObbState.canonicalPath);
3397                obbInfo = null;
3398            }
3399            if (obbInfo == null) {
3400                throw new IOException("Couldn't read OBB file: " + mObbState.canonicalPath);
3401            }
3402            return obbInfo;
3403        }
3404
3405        protected void sendNewStatusOrIgnore(int status) {
3406            if (mObbState == null || mObbState.token == null) {
3407                return;
3408            }
3409
3410            try {
3411                mObbState.token.onObbResult(mObbState.rawPath, mObbState.nonce, status);
3412            } catch (RemoteException e) {
3413                Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
3414            }
3415        }
3416    }
3417
3418    class MountObbAction extends ObbAction {
3419        private final String mKey;
3420        private final int mCallingUid;
3421
3422        MountObbAction(ObbState obbState, String key, int callingUid) {
3423            super(obbState);
3424            mKey = key;
3425            mCallingUid = callingUid;
3426        }
3427
3428        @Override
3429        public void handleExecute() throws IOException, RemoteException {
3430            waitForReady();
3431            warnOnNotMounted();
3432
3433            final ObbInfo obbInfo = getObbInfo();
3434
3435            if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mCallingUid)) {
3436                Slog.w(TAG, "Denied attempt to mount OBB " + obbInfo.filename
3437                        + " which is owned by " + obbInfo.packageName);
3438                sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
3439                return;
3440            }
3441
3442            final boolean isMounted;
3443            synchronized (mObbMounts) {
3444                isMounted = mObbPathToStateMap.containsKey(mObbState.rawPath);
3445            }
3446            if (isMounted) {
3447                Slog.w(TAG, "Attempt to mount OBB which is already mounted: " + obbInfo.filename);
3448                sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_ALREADY_MOUNTED);
3449                return;
3450            }
3451
3452            final String hashedKey;
3453            if (mKey == null) {
3454                hashedKey = "none";
3455            } else {
3456                try {
3457                    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
3458
3459                    KeySpec ks = new PBEKeySpec(mKey.toCharArray(), obbInfo.salt,
3460                            PBKDF2_HASH_ROUNDS, CRYPTO_ALGORITHM_KEY_SIZE);
3461                    SecretKey key = factory.generateSecret(ks);
3462                    BigInteger bi = new BigInteger(key.getEncoded());
3463                    hashedKey = bi.toString(16);
3464                } catch (NoSuchAlgorithmException e) {
3465                    Slog.e(TAG, "Could not load PBKDF2 algorithm", e);
3466                    sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
3467                    return;
3468                } catch (InvalidKeySpecException e) {
3469                    Slog.e(TAG, "Invalid key spec when loading PBKDF2 algorithm", e);
3470                    sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
3471                    return;
3472                }
3473            }
3474
3475            int rc = StorageResultCode.OperationSucceeded;
3476            try {
3477                mConnector.execute("obb", "mount", mObbState.canonicalPath, new SensitiveArg(hashedKey),
3478                        mObbState.ownerGid);
3479            } catch (NativeDaemonConnectorException e) {
3480                int code = e.getCode();
3481                if (code != VoldResponseCode.OpFailedStorageBusy) {
3482                    rc = StorageResultCode.OperationFailedInternalError;
3483                }
3484            }
3485
3486            if (rc == StorageResultCode.OperationSucceeded) {
3487                if (DEBUG_OBB)
3488                    Slog.d(TAG, "Successfully mounted OBB " + mObbState.canonicalPath);
3489
3490                synchronized (mObbMounts) {
3491                    addObbStateLocked(mObbState);
3492                }
3493
3494                sendNewStatusOrIgnore(OnObbStateChangeListener.MOUNTED);
3495            } else {
3496                Slog.e(TAG, "Couldn't mount OBB file: " + rc);
3497
3498                sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT);
3499            }
3500        }
3501
3502        @Override
3503        public void handleError() {
3504            sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
3505        }
3506
3507        @Override
3508        public String toString() {
3509            StringBuilder sb = new StringBuilder();
3510            sb.append("MountObbAction{");
3511            sb.append(mObbState);
3512            sb.append('}');
3513            return sb.toString();
3514        }
3515    }
3516
3517    class UnmountObbAction extends ObbAction {
3518        private final boolean mForceUnmount;
3519
3520        UnmountObbAction(ObbState obbState, boolean force) {
3521            super(obbState);
3522            mForceUnmount = force;
3523        }
3524
3525        @Override
3526        public void handleExecute() throws IOException {
3527            waitForReady();
3528            warnOnNotMounted();
3529
3530            final ObbState existingState;
3531            synchronized (mObbMounts) {
3532                existingState = mObbPathToStateMap.get(mObbState.rawPath);
3533            }
3534
3535            if (existingState == null) {
3536                sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_NOT_MOUNTED);
3537                return;
3538            }
3539
3540            if (existingState.ownerGid != mObbState.ownerGid) {
3541                Slog.w(TAG, "Permission denied attempting to unmount OBB " + existingState.rawPath
3542                        + " (owned by GID " + existingState.ownerGid + ")");
3543                sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
3544                return;
3545            }
3546
3547            int rc = StorageResultCode.OperationSucceeded;
3548            try {
3549                final Command cmd = new Command("obb", "unmount", mObbState.canonicalPath);
3550                if (mForceUnmount) {
3551                    cmd.appendArg("force");
3552                }
3553                mConnector.execute(cmd);
3554            } catch (NativeDaemonConnectorException e) {
3555                int code = e.getCode();
3556                if (code == VoldResponseCode.OpFailedStorageBusy) {
3557                    rc = StorageResultCode.OperationFailedStorageBusy;
3558                } else if (code == VoldResponseCode.OpFailedStorageNotFound) {
3559                    // If it's not mounted then we've already won.
3560                    rc = StorageResultCode.OperationSucceeded;
3561                } else {
3562                    rc = StorageResultCode.OperationFailedInternalError;
3563                }
3564            }
3565
3566            if (rc == StorageResultCode.OperationSucceeded) {
3567                synchronized (mObbMounts) {
3568                    removeObbStateLocked(existingState);
3569                }
3570
3571                sendNewStatusOrIgnore(OnObbStateChangeListener.UNMOUNTED);
3572            } else {
3573                Slog.w(TAG, "Could not unmount OBB: " + existingState);
3574                sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_UNMOUNT);
3575            }
3576        }
3577
3578        @Override
3579        public void handleError() {
3580            sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
3581        }
3582
3583        @Override
3584        public String toString() {
3585            StringBuilder sb = new StringBuilder();
3586            sb.append("UnmountObbAction{");
3587            sb.append(mObbState);
3588            sb.append(",force=");
3589            sb.append(mForceUnmount);
3590            sb.append('}');
3591            return sb.toString();
3592        }
3593    }
3594
3595    private static class Callbacks extends Handler {
3596        private static final int MSG_STORAGE_STATE_CHANGED = 1;
3597        private static final int MSG_VOLUME_STATE_CHANGED = 2;
3598        private static final int MSG_VOLUME_RECORD_CHANGED = 3;
3599        private static final int MSG_VOLUME_FORGOTTEN = 4;
3600        private static final int MSG_DISK_SCANNED = 5;
3601        private static final int MSG_DISK_DESTROYED = 6;
3602
3603        private final RemoteCallbackList<IMountServiceListener>
3604                mCallbacks = new RemoteCallbackList<>();
3605
3606        public Callbacks(Looper looper) {
3607            super(looper);
3608        }
3609
3610        public void register(IMountServiceListener callback) {
3611            mCallbacks.register(callback);
3612        }
3613
3614        public void unregister(IMountServiceListener callback) {
3615            mCallbacks.unregister(callback);
3616        }
3617
3618        @Override
3619        public void handleMessage(Message msg) {
3620            final SomeArgs args = (SomeArgs) msg.obj;
3621            final int n = mCallbacks.beginBroadcast();
3622            for (int i = 0; i < n; i++) {
3623                final IMountServiceListener callback = mCallbacks.getBroadcastItem(i);
3624                try {
3625                    invokeCallback(callback, msg.what, args);
3626                } catch (RemoteException ignored) {
3627                }
3628            }
3629            mCallbacks.finishBroadcast();
3630            args.recycle();
3631        }
3632
3633        private void invokeCallback(IMountServiceListener callback, int what, SomeArgs args)
3634                throws RemoteException {
3635            switch (what) {
3636                case MSG_STORAGE_STATE_CHANGED: {
3637                    callback.onStorageStateChanged((String) args.arg1, (String) args.arg2,
3638                            (String) args.arg3);
3639                    break;
3640                }
3641                case MSG_VOLUME_STATE_CHANGED: {
3642                    callback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3);
3643                    break;
3644                }
3645                case MSG_VOLUME_RECORD_CHANGED: {
3646                    callback.onVolumeRecordChanged((VolumeRecord) args.arg1);
3647                    break;
3648                }
3649                case MSG_VOLUME_FORGOTTEN: {
3650                    callback.onVolumeForgotten((String) args.arg1);
3651                    break;
3652                }
3653                case MSG_DISK_SCANNED: {
3654                    callback.onDiskScanned((DiskInfo) args.arg1, args.argi2);
3655                    break;
3656                }
3657                case MSG_DISK_DESTROYED: {
3658                    callback.onDiskDestroyed((DiskInfo) args.arg1);
3659                    break;
3660                }
3661            }
3662        }
3663
3664        private void notifyStorageStateChanged(String path, String oldState, String newState) {
3665            final SomeArgs args = SomeArgs.obtain();
3666            args.arg1 = path;
3667            args.arg2 = oldState;
3668            args.arg3 = newState;
3669            obtainMessage(MSG_STORAGE_STATE_CHANGED, args).sendToTarget();
3670        }
3671
3672        private void notifyVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
3673            final SomeArgs args = SomeArgs.obtain();
3674            args.arg1 = vol.clone();
3675            args.argi2 = oldState;
3676            args.argi3 = newState;
3677            obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget();
3678        }
3679
3680        private void notifyVolumeRecordChanged(VolumeRecord rec) {
3681            final SomeArgs args = SomeArgs.obtain();
3682            args.arg1 = rec.clone();
3683            obtainMessage(MSG_VOLUME_RECORD_CHANGED, args).sendToTarget();
3684        }
3685
3686        private void notifyVolumeForgotten(String fsUuid) {
3687            final SomeArgs args = SomeArgs.obtain();
3688            args.arg1 = fsUuid;
3689            obtainMessage(MSG_VOLUME_FORGOTTEN, args).sendToTarget();
3690        }
3691
3692        private void notifyDiskScanned(DiskInfo disk, int volumeCount) {
3693            final SomeArgs args = SomeArgs.obtain();
3694            args.arg1 = disk.clone();
3695            args.argi2 = volumeCount;
3696            obtainMessage(MSG_DISK_SCANNED, args).sendToTarget();
3697        }
3698
3699        private void notifyDiskDestroyed(DiskInfo disk) {
3700            final SomeArgs args = SomeArgs.obtain();
3701            args.arg1 = disk.clone();
3702            obtainMessage(MSG_DISK_DESTROYED, args).sendToTarget();
3703        }
3704    }
3705
3706    @Override
3707    protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
3708        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
3709
3710        final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ", 160);
3711        synchronized (mLock) {
3712            pw.println("Disks:");
3713            pw.increaseIndent();
3714            for (int i = 0; i < mDisks.size(); i++) {
3715                final DiskInfo disk = mDisks.valueAt(i);
3716                disk.dump(pw);
3717            }
3718            pw.decreaseIndent();
3719
3720            pw.println();
3721            pw.println("Volumes:");
3722            pw.increaseIndent();
3723            for (int i = 0; i < mVolumes.size(); i++) {
3724                final VolumeInfo vol = mVolumes.valueAt(i);
3725                if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.id)) continue;
3726                vol.dump(pw);
3727            }
3728            pw.decreaseIndent();
3729
3730            pw.println();
3731            pw.println("Records:");
3732            pw.increaseIndent();
3733            for (int i = 0; i < mRecords.size(); i++) {
3734                final VolumeRecord note = mRecords.valueAt(i);
3735                note.dump(pw);
3736            }
3737            pw.decreaseIndent();
3738
3739            pw.println();
3740            pw.println("Primary storage UUID: " + mPrimaryStorageUuid);
3741            pw.println("Force adoptable: " + mForceAdoptable);
3742            pw.println();
3743            pw.println("Local unlocked users: " + Arrays.toString(mLocalUnlockedUsers));
3744            pw.println("System unlocked users: " + Arrays.toString(mSystemUnlockedUsers));
3745        }
3746
3747        synchronized (mObbMounts) {
3748            pw.println();
3749            pw.println("mObbMounts:");
3750            pw.increaseIndent();
3751            final Iterator<Entry<IBinder, List<ObbState>>> binders = mObbMounts.entrySet()
3752                    .iterator();
3753            while (binders.hasNext()) {
3754                Entry<IBinder, List<ObbState>> e = binders.next();
3755                pw.println(e.getKey() + ":");
3756                pw.increaseIndent();
3757                final List<ObbState> obbStates = e.getValue();
3758                for (final ObbState obbState : obbStates) {
3759                    pw.println(obbState);
3760                }
3761                pw.decreaseIndent();
3762            }
3763            pw.decreaseIndent();
3764
3765            pw.println();
3766            pw.println("mObbPathToStateMap:");
3767            pw.increaseIndent();
3768            final Iterator<Entry<String, ObbState>> maps = mObbPathToStateMap.entrySet().iterator();
3769            while (maps.hasNext()) {
3770                final Entry<String, ObbState> e = maps.next();
3771                pw.print(e.getKey());
3772                pw.print(" -> ");
3773                pw.println(e.getValue());
3774            }
3775            pw.decreaseIndent();
3776        }
3777
3778        pw.println();
3779        pw.println("mConnector:");
3780        pw.increaseIndent();
3781        mConnector.dump(fd, pw, args);
3782        pw.decreaseIndent();
3783
3784        pw.println();
3785        pw.println("mCryptConnector:");
3786        pw.increaseIndent();
3787        mCryptConnector.dump(fd, pw, args);
3788        pw.decreaseIndent();
3789
3790        pw.println();
3791        pw.print("Last maintenance: ");
3792        pw.println(TimeUtils.formatForLogging(mLastMaintenance));
3793    }
3794
3795    /** {@inheritDoc} */
3796    @Override
3797    public void monitor() {
3798        if (mConnector != null) {
3799            mConnector.monitor();
3800        }
3801        if (mCryptConnector != null) {
3802            mCryptConnector.monitor();
3803        }
3804    }
3805
3806    private final class MountServiceInternalImpl extends MountServiceInternal {
3807        // Not guarded by a lock.
3808        private final CopyOnWriteArrayList<ExternalStorageMountPolicy> mPolicies =
3809                new CopyOnWriteArrayList<>();
3810
3811        @Override
3812        public void addExternalStoragePolicy(ExternalStorageMountPolicy policy) {
3813            // No locking - CopyOnWriteArrayList
3814            mPolicies.add(policy);
3815        }
3816
3817        @Override
3818        public void onExternalStoragePolicyChanged(int uid, String packageName) {
3819            final int mountMode = getExternalStorageMountMode(uid, packageName);
3820            remountUidExternalStorage(uid, mountMode);
3821        }
3822
3823        @Override
3824        public int getExternalStorageMountMode(int uid, String packageName) {
3825            // No locking - CopyOnWriteArrayList
3826            int mountMode = Integer.MAX_VALUE;
3827            for (ExternalStorageMountPolicy policy : mPolicies) {
3828                final int policyMode = policy.getMountMode(uid, packageName);
3829                if (policyMode == Zygote.MOUNT_EXTERNAL_NONE) {
3830                    return Zygote.MOUNT_EXTERNAL_NONE;
3831                }
3832                mountMode = Math.min(mountMode, policyMode);
3833            }
3834            if (mountMode == Integer.MAX_VALUE) {
3835                return Zygote.MOUNT_EXTERNAL_NONE;
3836            }
3837            return mountMode;
3838        }
3839
3840        public boolean hasExternalStorage(int uid, String packageName) {
3841            // No need to check for system uid. This avoids a deadlock between
3842            // PackageManagerService and AppOpsService.
3843            if (uid == Process.SYSTEM_UID) {
3844                return true;
3845            }
3846            // No locking - CopyOnWriteArrayList
3847            for (ExternalStorageMountPolicy policy : mPolicies) {
3848                final boolean policyHasStorage = policy.hasExternalStorage(uid, packageName);
3849                if (!policyHasStorage) {
3850                    return false;
3851                }
3852            }
3853            return true;
3854        }
3855    }
3856}
3857