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