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