MountService.java revision fcf1e55821b694df3b8434f40aa3b6d3c3e7ea50
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        // When a user has secure lock screen, require a challenge token to
2820        // actually unlock. This check is mostly in place for emulation mode.
2821        if (mLockPatternUtils.isSecure(userId) && ArrayUtils.isEmpty(token)) {
2822            throw new IllegalStateException("Token required to unlock secure user " + userId);
2823        }
2824
2825        try {
2826            mCryptConnector.execute("cryptfs", "unlock_user_key", userId, serialNumber,
2827                    encodeBytes(token), encodeBytes(secret));
2828        } catch (NativeDaemonConnectorException e) {
2829            throw e.rethrowAsParcelableException();
2830        }
2831
2832        synchronized (mLock) {
2833            mLocalUnlockedUsers = ArrayUtils.appendInt(mLocalUnlockedUsers, userId);
2834        }
2835    }
2836
2837    @Override
2838    public void lockUserKey(int userId) {
2839        enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
2840        waitForReady();
2841
2842        try {
2843            mCryptConnector.execute("cryptfs", "lock_user_key", userId);
2844        } catch (NativeDaemonConnectorException e) {
2845            throw e.rethrowAsParcelableException();
2846        }
2847
2848        synchronized (mLock) {
2849            mLocalUnlockedUsers = ArrayUtils.removeInt(mLocalUnlockedUsers, userId);
2850        }
2851    }
2852
2853    @Override
2854    public boolean isUserKeyUnlocked(int userId) {
2855        if (StorageManager.isFileEncryptedNativeOrEmulated()) {
2856            synchronized (mLock) {
2857                return ArrayUtils.contains(mLocalUnlockedUsers, userId);
2858            }
2859        } else {
2860            return true;
2861        }
2862    }
2863
2864    @Override
2865    public void prepareUserStorage(String volumeUuid, int userId, int serialNumber, int flags) {
2866        enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
2867        waitForReady();
2868
2869        try {
2870            mCryptConnector.execute("cryptfs", "prepare_user_storage", escapeNull(volumeUuid),
2871                    userId, serialNumber, flags);
2872        } catch (NativeDaemonConnectorException e) {
2873            throw e.rethrowAsParcelableException();
2874        }
2875    }
2876
2877    @Override
2878    public void destroyUserStorage(String volumeUuid, int userId, int flags) {
2879        enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
2880        waitForReady();
2881
2882        try {
2883            mCryptConnector.execute("cryptfs", "destroy_user_storage", escapeNull(volumeUuid),
2884                    userId, flags);
2885        } catch (NativeDaemonConnectorException e) {
2886            throw e.rethrowAsParcelableException();
2887        }
2888    }
2889
2890    @Override
2891    public ParcelFileDescriptor mountAppFuse(final String name) throws RemoteException {
2892        try {
2893            final int uid = Binder.getCallingUid();
2894            final int pid = Binder.getCallingPid();
2895            final NativeDaemonEvent event =
2896                    mConnector.execute("appfuse", "mount", uid, pid, name);
2897            if (event.getFileDescriptors() == null) {
2898                throw new RemoteException("AppFuse FD from vold is null.");
2899            }
2900            return ParcelFileDescriptor.fromFd(
2901                    event.getFileDescriptors()[0],
2902                    mHandler,
2903                    new ParcelFileDescriptor.OnCloseListener() {
2904                        @Override
2905                        public void onClose(IOException e) {
2906                            try {
2907                                final NativeDaemonEvent event = mConnector.execute(
2908                                        "appfuse", "unmount", uid, pid, name);
2909                            } catch (NativeDaemonConnectorException unmountException) {
2910                                Log.e(TAG, "Failed to unmount appfuse.");
2911                            }
2912                        }
2913                    });
2914        } catch (NativeDaemonConnectorException e) {
2915            throw e.rethrowAsParcelableException();
2916        } catch (IOException e) {
2917            throw new RemoteException(e.getMessage());
2918        }
2919    }
2920
2921    @Override
2922    public int mkdirs(String callingPkg, String appPath) {
2923        final int userId = UserHandle.getUserId(Binder.getCallingUid());
2924        final UserEnvironment userEnv = new UserEnvironment(userId);
2925
2926        // Validate that reported package name belongs to caller
2927        final AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(
2928                Context.APP_OPS_SERVICE);
2929        appOps.checkPackage(Binder.getCallingUid(), callingPkg);
2930
2931        File appFile = null;
2932        try {
2933            appFile = new File(appPath).getCanonicalFile();
2934        } catch (IOException e) {
2935            Slog.e(TAG, "Failed to resolve " + appPath + ": " + e);
2936            return -1;
2937        }
2938
2939        // Try translating the app path into a vold path, but require that it
2940        // belong to the calling package.
2941        if (FileUtils.contains(userEnv.buildExternalStorageAppDataDirs(callingPkg), appFile) ||
2942                FileUtils.contains(userEnv.buildExternalStorageAppObbDirs(callingPkg), appFile) ||
2943                FileUtils.contains(userEnv.buildExternalStorageAppMediaDirs(callingPkg), appFile)) {
2944            appPath = appFile.getAbsolutePath();
2945            if (!appPath.endsWith("/")) {
2946                appPath = appPath + "/";
2947            }
2948
2949            try {
2950                mConnector.execute("volume", "mkdirs", appPath);
2951                return 0;
2952            } catch (NativeDaemonConnectorException e) {
2953                return e.getCode();
2954            }
2955        }
2956
2957        throw new SecurityException("Invalid mkdirs path: " + appFile);
2958    }
2959
2960    @Override
2961    public StorageVolume[] getVolumeList(int uid, String packageName, int flags) {
2962        final int userId = UserHandle.getUserId(uid);
2963
2964        final boolean forWrite = (flags & StorageManager.FLAG_FOR_WRITE) != 0;
2965        final boolean realState = (flags & StorageManager.FLAG_REAL_STATE) != 0;
2966        final boolean includeInvisible = (flags & StorageManager.FLAG_INCLUDE_INVISIBLE) != 0;
2967
2968        final boolean userKeyUnlocked;
2969        final boolean storagePermission;
2970        final long token = Binder.clearCallingIdentity();
2971        try {
2972            userKeyUnlocked = isUserKeyUnlocked(userId);
2973            storagePermission = mMountServiceInternal.hasExternalStorage(uid, packageName);
2974        } finally {
2975            Binder.restoreCallingIdentity(token);
2976        }
2977
2978        boolean foundPrimary = false;
2979
2980        final ArrayList<StorageVolume> res = new ArrayList<>();
2981        synchronized (mLock) {
2982            for (int i = 0; i < mVolumes.size(); i++) {
2983                final VolumeInfo vol = mVolumes.valueAt(i);
2984                switch (vol.getType()) {
2985                    case VolumeInfo.TYPE_PUBLIC:
2986                    case VolumeInfo.TYPE_EMULATED:
2987                        break;
2988                    default:
2989                        continue;
2990                }
2991
2992                boolean match = false;
2993                if (forWrite) {
2994                    match = vol.isVisibleForWrite(userId);
2995                } else {
2996                    match = vol.isVisibleForRead(userId) || includeInvisible;
2997                }
2998                if (!match) continue;
2999
3000                boolean reportUnmounted = false;
3001                if ((vol.getType() == VolumeInfo.TYPE_EMULATED) && !userKeyUnlocked) {
3002                    reportUnmounted = true;
3003                } else if (!storagePermission && !realState) {
3004                    reportUnmounted = true;
3005                }
3006
3007                final StorageVolume userVol = vol.buildStorageVolume(mContext, userId,
3008                        reportUnmounted);
3009                if (vol.isPrimary()) {
3010                    res.add(0, userVol);
3011                    foundPrimary = true;
3012                } else {
3013                    res.add(userVol);
3014                }
3015            }
3016        }
3017
3018        if (!foundPrimary) {
3019            Log.w(TAG, "No primary storage defined yet; hacking together a stub");
3020
3021            final boolean primaryPhysical = SystemProperties.getBoolean(
3022                    StorageManager.PROP_PRIMARY_PHYSICAL, false);
3023
3024            final String id = "stub_primary";
3025            final File path = Environment.getLegacyExternalStorageDirectory();
3026            final String description = mContext.getString(android.R.string.unknownName);
3027            final boolean primary = true;
3028            final boolean removable = primaryPhysical;
3029            final boolean emulated = !primaryPhysical;
3030            final long mtpReserveSize = 0L;
3031            final boolean allowMassStorage = false;
3032            final long maxFileSize = 0L;
3033            final UserHandle owner = new UserHandle(userId);
3034            final String uuid = null;
3035            final String state = Environment.MEDIA_REMOVED;
3036
3037            res.add(0, new StorageVolume(id, StorageVolume.STORAGE_ID_INVALID, path,
3038                    description, primary, removable, emulated, mtpReserveSize,
3039                    allowMassStorage, maxFileSize, owner, uuid, state));
3040        }
3041
3042        return res.toArray(new StorageVolume[res.size()]);
3043    }
3044
3045    @Override
3046    public DiskInfo[] getDisks() {
3047        synchronized (mLock) {
3048            final DiskInfo[] res = new DiskInfo[mDisks.size()];
3049            for (int i = 0; i < mDisks.size(); i++) {
3050                res[i] = mDisks.valueAt(i);
3051            }
3052            return res;
3053        }
3054    }
3055
3056    @Override
3057    public VolumeInfo[] getVolumes(int flags) {
3058        synchronized (mLock) {
3059            final VolumeInfo[] res = new VolumeInfo[mVolumes.size()];
3060            for (int i = 0; i < mVolumes.size(); i++) {
3061                res[i] = mVolumes.valueAt(i);
3062            }
3063            return res;
3064        }
3065    }
3066
3067    @Override
3068    public VolumeRecord[] getVolumeRecords(int flags) {
3069        synchronized (mLock) {
3070            final VolumeRecord[] res = new VolumeRecord[mRecords.size()];
3071            for (int i = 0; i < mRecords.size(); i++) {
3072                res[i] = mRecords.valueAt(i);
3073            }
3074            return res;
3075        }
3076    }
3077
3078    private void addObbStateLocked(ObbState obbState) throws RemoteException {
3079        final IBinder binder = obbState.getBinder();
3080        List<ObbState> obbStates = mObbMounts.get(binder);
3081
3082        if (obbStates == null) {
3083            obbStates = new ArrayList<ObbState>();
3084            mObbMounts.put(binder, obbStates);
3085        } else {
3086            for (final ObbState o : obbStates) {
3087                if (o.rawPath.equals(obbState.rawPath)) {
3088                    throw new IllegalStateException("Attempt to add ObbState twice. "
3089                            + "This indicates an error in the MountService logic.");
3090                }
3091            }
3092        }
3093
3094        obbStates.add(obbState);
3095        try {
3096            obbState.link();
3097        } catch (RemoteException e) {
3098            /*
3099             * The binder died before we could link it, so clean up our state
3100             * and return failure.
3101             */
3102            obbStates.remove(obbState);
3103            if (obbStates.isEmpty()) {
3104                mObbMounts.remove(binder);
3105            }
3106
3107            // Rethrow the error so mountObb can get it
3108            throw e;
3109        }
3110
3111        mObbPathToStateMap.put(obbState.rawPath, obbState);
3112    }
3113
3114    private void removeObbStateLocked(ObbState obbState) {
3115        final IBinder binder = obbState.getBinder();
3116        final List<ObbState> obbStates = mObbMounts.get(binder);
3117        if (obbStates != null) {
3118            if (obbStates.remove(obbState)) {
3119                obbState.unlink();
3120            }
3121            if (obbStates.isEmpty()) {
3122                mObbMounts.remove(binder);
3123            }
3124        }
3125
3126        mObbPathToStateMap.remove(obbState.rawPath);
3127    }
3128
3129    private class ObbActionHandler extends Handler {
3130        private boolean mBound = false;
3131        private final List<ObbAction> mActions = new LinkedList<ObbAction>();
3132
3133        ObbActionHandler(Looper l) {
3134            super(l);
3135        }
3136
3137        @Override
3138        public void handleMessage(Message msg) {
3139            switch (msg.what) {
3140                case OBB_RUN_ACTION: {
3141                    final ObbAction action = (ObbAction) msg.obj;
3142
3143                    if (DEBUG_OBB)
3144                        Slog.i(TAG, "OBB_RUN_ACTION: " + action.toString());
3145
3146                    // If a bind was already initiated we don't really
3147                    // need to do anything. The pending install
3148                    // will be processed later on.
3149                    if (!mBound) {
3150                        // If this is the only one pending we might
3151                        // have to bind to the service again.
3152                        if (!connectToService()) {
3153                            Slog.e(TAG, "Failed to bind to media container service");
3154                            action.handleError();
3155                            return;
3156                        }
3157                    }
3158
3159                    mActions.add(action);
3160                    break;
3161                }
3162                case OBB_MCS_BOUND: {
3163                    if (DEBUG_OBB)
3164                        Slog.i(TAG, "OBB_MCS_BOUND");
3165                    if (msg.obj != null) {
3166                        mContainerService = (IMediaContainerService) msg.obj;
3167                    }
3168                    if (mContainerService == null) {
3169                        // Something seriously wrong. Bail out
3170                        Slog.e(TAG, "Cannot bind to media container service");
3171                        for (ObbAction action : mActions) {
3172                            // Indicate service bind error
3173                            action.handleError();
3174                        }
3175                        mActions.clear();
3176                    } else if (mActions.size() > 0) {
3177                        final ObbAction action = mActions.get(0);
3178                        if (action != null) {
3179                            action.execute(this);
3180                        }
3181                    } else {
3182                        // Should never happen ideally.
3183                        Slog.w(TAG, "Empty queue");
3184                    }
3185                    break;
3186                }
3187                case OBB_MCS_RECONNECT: {
3188                    if (DEBUG_OBB)
3189                        Slog.i(TAG, "OBB_MCS_RECONNECT");
3190                    if (mActions.size() > 0) {
3191                        if (mBound) {
3192                            disconnectService();
3193                        }
3194                        if (!connectToService()) {
3195                            Slog.e(TAG, "Failed to bind to media container service");
3196                            for (ObbAction action : mActions) {
3197                                // Indicate service bind error
3198                                action.handleError();
3199                            }
3200                            mActions.clear();
3201                        }
3202                    }
3203                    break;
3204                }
3205                case OBB_MCS_UNBIND: {
3206                    if (DEBUG_OBB)
3207                        Slog.i(TAG, "OBB_MCS_UNBIND");
3208
3209                    // Delete pending install
3210                    if (mActions.size() > 0) {
3211                        mActions.remove(0);
3212                    }
3213                    if (mActions.size() == 0) {
3214                        if (mBound) {
3215                            disconnectService();
3216                        }
3217                    } else {
3218                        // There are more pending requests in queue.
3219                        // Just post MCS_BOUND message to trigger processing
3220                        // of next pending install.
3221                        mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND);
3222                    }
3223                    break;
3224                }
3225                case OBB_FLUSH_MOUNT_STATE: {
3226                    final String path = (String) msg.obj;
3227
3228                    if (DEBUG_OBB)
3229                        Slog.i(TAG, "Flushing all OBB state for path " + path);
3230
3231                    synchronized (mObbMounts) {
3232                        final List<ObbState> obbStatesToRemove = new LinkedList<ObbState>();
3233
3234                        final Iterator<ObbState> i = mObbPathToStateMap.values().iterator();
3235                        while (i.hasNext()) {
3236                            final ObbState state = i.next();
3237
3238                            /*
3239                             * If this entry's source file is in the volume path
3240                             * that got unmounted, remove it because it's no
3241                             * longer valid.
3242                             */
3243                            if (state.canonicalPath.startsWith(path)) {
3244                                obbStatesToRemove.add(state);
3245                            }
3246                        }
3247
3248                        for (final ObbState obbState : obbStatesToRemove) {
3249                            if (DEBUG_OBB)
3250                                Slog.i(TAG, "Removing state for " + obbState.rawPath);
3251
3252                            removeObbStateLocked(obbState);
3253
3254                            try {
3255                                obbState.token.onObbResult(obbState.rawPath, obbState.nonce,
3256                                        OnObbStateChangeListener.UNMOUNTED);
3257                            } catch (RemoteException e) {
3258                                Slog.i(TAG, "Couldn't send unmount notification for  OBB: "
3259                                        + obbState.rawPath);
3260                            }
3261                        }
3262                    }
3263                    break;
3264                }
3265            }
3266        }
3267
3268        private boolean connectToService() {
3269            if (DEBUG_OBB)
3270                Slog.i(TAG, "Trying to bind to DefaultContainerService");
3271
3272            Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
3273            if (mContext.bindServiceAsUser(service, mDefContainerConn, Context.BIND_AUTO_CREATE,
3274                    UserHandle.SYSTEM)) {
3275                mBound = true;
3276                return true;
3277            }
3278            return false;
3279        }
3280
3281        private void disconnectService() {
3282            mContainerService = null;
3283            mBound = false;
3284            mContext.unbindService(mDefContainerConn);
3285        }
3286    }
3287
3288    abstract class ObbAction {
3289        private static final int MAX_RETRIES = 3;
3290        private int mRetries;
3291
3292        ObbState mObbState;
3293
3294        ObbAction(ObbState obbState) {
3295            mObbState = obbState;
3296        }
3297
3298        public void execute(ObbActionHandler handler) {
3299            try {
3300                if (DEBUG_OBB)
3301                    Slog.i(TAG, "Starting to execute action: " + toString());
3302                mRetries++;
3303                if (mRetries > MAX_RETRIES) {
3304                    Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
3305                    mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
3306                    handleError();
3307                } else {
3308                    handleExecute();
3309                    if (DEBUG_OBB)
3310                        Slog.i(TAG, "Posting install MCS_UNBIND");
3311                    mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
3312                }
3313            } catch (RemoteException e) {
3314                if (DEBUG_OBB)
3315                    Slog.i(TAG, "Posting install MCS_RECONNECT");
3316                mObbActionHandler.sendEmptyMessage(OBB_MCS_RECONNECT);
3317            } catch (Exception e) {
3318                if (DEBUG_OBB)
3319                    Slog.d(TAG, "Error handling OBB action", e);
3320                handleError();
3321                mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
3322            }
3323        }
3324
3325        abstract void handleExecute() throws RemoteException, IOException;
3326        abstract void handleError();
3327
3328        protected ObbInfo getObbInfo() throws IOException {
3329            ObbInfo obbInfo;
3330            try {
3331                obbInfo = mContainerService.getObbInfo(mObbState.canonicalPath);
3332            } catch (RemoteException e) {
3333                Slog.d(TAG, "Couldn't call DefaultContainerService to fetch OBB info for "
3334                        + mObbState.canonicalPath);
3335                obbInfo = null;
3336            }
3337            if (obbInfo == null) {
3338                throw new IOException("Couldn't read OBB file: " + mObbState.canonicalPath);
3339            }
3340            return obbInfo;
3341        }
3342
3343        protected void sendNewStatusOrIgnore(int status) {
3344            if (mObbState == null || mObbState.token == null) {
3345                return;
3346            }
3347
3348            try {
3349                mObbState.token.onObbResult(mObbState.rawPath, mObbState.nonce, status);
3350            } catch (RemoteException e) {
3351                Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
3352            }
3353        }
3354    }
3355
3356    class MountObbAction extends ObbAction {
3357        private final String mKey;
3358        private final int mCallingUid;
3359
3360        MountObbAction(ObbState obbState, String key, int callingUid) {
3361            super(obbState);
3362            mKey = key;
3363            mCallingUid = callingUid;
3364        }
3365
3366        @Override
3367        public void handleExecute() throws IOException, RemoteException {
3368            waitForReady();
3369            warnOnNotMounted();
3370
3371            final ObbInfo obbInfo = getObbInfo();
3372
3373            if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mCallingUid)) {
3374                Slog.w(TAG, "Denied attempt to mount OBB " + obbInfo.filename
3375                        + " which is owned by " + obbInfo.packageName);
3376                sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
3377                return;
3378            }
3379
3380            final boolean isMounted;
3381            synchronized (mObbMounts) {
3382                isMounted = mObbPathToStateMap.containsKey(mObbState.rawPath);
3383            }
3384            if (isMounted) {
3385                Slog.w(TAG, "Attempt to mount OBB which is already mounted: " + obbInfo.filename);
3386                sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_ALREADY_MOUNTED);
3387                return;
3388            }
3389
3390            final String hashedKey;
3391            if (mKey == null) {
3392                hashedKey = "none";
3393            } else {
3394                try {
3395                    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
3396
3397                    KeySpec ks = new PBEKeySpec(mKey.toCharArray(), obbInfo.salt,
3398                            PBKDF2_HASH_ROUNDS, CRYPTO_ALGORITHM_KEY_SIZE);
3399                    SecretKey key = factory.generateSecret(ks);
3400                    BigInteger bi = new BigInteger(key.getEncoded());
3401                    hashedKey = bi.toString(16);
3402                } catch (NoSuchAlgorithmException e) {
3403                    Slog.e(TAG, "Could not load PBKDF2 algorithm", e);
3404                    sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
3405                    return;
3406                } catch (InvalidKeySpecException e) {
3407                    Slog.e(TAG, "Invalid key spec when loading PBKDF2 algorithm", e);
3408                    sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
3409                    return;
3410                }
3411            }
3412
3413            int rc = StorageResultCode.OperationSucceeded;
3414            try {
3415                mConnector.execute("obb", "mount", mObbState.canonicalPath, new SensitiveArg(hashedKey),
3416                        mObbState.ownerGid);
3417            } catch (NativeDaemonConnectorException e) {
3418                int code = e.getCode();
3419                if (code != VoldResponseCode.OpFailedStorageBusy) {
3420                    rc = StorageResultCode.OperationFailedInternalError;
3421                }
3422            }
3423
3424            if (rc == StorageResultCode.OperationSucceeded) {
3425                if (DEBUG_OBB)
3426                    Slog.d(TAG, "Successfully mounted OBB " + mObbState.canonicalPath);
3427
3428                synchronized (mObbMounts) {
3429                    addObbStateLocked(mObbState);
3430                }
3431
3432                sendNewStatusOrIgnore(OnObbStateChangeListener.MOUNTED);
3433            } else {
3434                Slog.e(TAG, "Couldn't mount OBB file: " + rc);
3435
3436                sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT);
3437            }
3438        }
3439
3440        @Override
3441        public void handleError() {
3442            sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
3443        }
3444
3445        @Override
3446        public String toString() {
3447            StringBuilder sb = new StringBuilder();
3448            sb.append("MountObbAction{");
3449            sb.append(mObbState);
3450            sb.append('}');
3451            return sb.toString();
3452        }
3453    }
3454
3455    class UnmountObbAction extends ObbAction {
3456        private final boolean mForceUnmount;
3457
3458        UnmountObbAction(ObbState obbState, boolean force) {
3459            super(obbState);
3460            mForceUnmount = force;
3461        }
3462
3463        @Override
3464        public void handleExecute() throws IOException {
3465            waitForReady();
3466            warnOnNotMounted();
3467
3468            final ObbState existingState;
3469            synchronized (mObbMounts) {
3470                existingState = mObbPathToStateMap.get(mObbState.rawPath);
3471            }
3472
3473            if (existingState == null) {
3474                sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_NOT_MOUNTED);
3475                return;
3476            }
3477
3478            if (existingState.ownerGid != mObbState.ownerGid) {
3479                Slog.w(TAG, "Permission denied attempting to unmount OBB " + existingState.rawPath
3480                        + " (owned by GID " + existingState.ownerGid + ")");
3481                sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
3482                return;
3483            }
3484
3485            int rc = StorageResultCode.OperationSucceeded;
3486            try {
3487                final Command cmd = new Command("obb", "unmount", mObbState.canonicalPath);
3488                if (mForceUnmount) {
3489                    cmd.appendArg("force");
3490                }
3491                mConnector.execute(cmd);
3492            } catch (NativeDaemonConnectorException e) {
3493                int code = e.getCode();
3494                if (code == VoldResponseCode.OpFailedStorageBusy) {
3495                    rc = StorageResultCode.OperationFailedStorageBusy;
3496                } else if (code == VoldResponseCode.OpFailedStorageNotFound) {
3497                    // If it's not mounted then we've already won.
3498                    rc = StorageResultCode.OperationSucceeded;
3499                } else {
3500                    rc = StorageResultCode.OperationFailedInternalError;
3501                }
3502            }
3503
3504            if (rc == StorageResultCode.OperationSucceeded) {
3505                synchronized (mObbMounts) {
3506                    removeObbStateLocked(existingState);
3507                }
3508
3509                sendNewStatusOrIgnore(OnObbStateChangeListener.UNMOUNTED);
3510            } else {
3511                Slog.w(TAG, "Could not unmount OBB: " + existingState);
3512                sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_UNMOUNT);
3513            }
3514        }
3515
3516        @Override
3517        public void handleError() {
3518            sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
3519        }
3520
3521        @Override
3522        public String toString() {
3523            StringBuilder sb = new StringBuilder();
3524            sb.append("UnmountObbAction{");
3525            sb.append(mObbState);
3526            sb.append(",force=");
3527            sb.append(mForceUnmount);
3528            sb.append('}');
3529            return sb.toString();
3530        }
3531    }
3532
3533    private static class Callbacks extends Handler {
3534        private static final int MSG_STORAGE_STATE_CHANGED = 1;
3535        private static final int MSG_VOLUME_STATE_CHANGED = 2;
3536        private static final int MSG_VOLUME_RECORD_CHANGED = 3;
3537        private static final int MSG_VOLUME_FORGOTTEN = 4;
3538        private static final int MSG_DISK_SCANNED = 5;
3539        private static final int MSG_DISK_DESTROYED = 6;
3540
3541        private final RemoteCallbackList<IMountServiceListener>
3542                mCallbacks = new RemoteCallbackList<>();
3543
3544        public Callbacks(Looper looper) {
3545            super(looper);
3546        }
3547
3548        public void register(IMountServiceListener callback) {
3549            mCallbacks.register(callback);
3550        }
3551
3552        public void unregister(IMountServiceListener callback) {
3553            mCallbacks.unregister(callback);
3554        }
3555
3556        @Override
3557        public void handleMessage(Message msg) {
3558            final SomeArgs args = (SomeArgs) msg.obj;
3559            final int n = mCallbacks.beginBroadcast();
3560            for (int i = 0; i < n; i++) {
3561                final IMountServiceListener callback = mCallbacks.getBroadcastItem(i);
3562                try {
3563                    invokeCallback(callback, msg.what, args);
3564                } catch (RemoteException ignored) {
3565                }
3566            }
3567            mCallbacks.finishBroadcast();
3568            args.recycle();
3569        }
3570
3571        private void invokeCallback(IMountServiceListener callback, int what, SomeArgs args)
3572                throws RemoteException {
3573            switch (what) {
3574                case MSG_STORAGE_STATE_CHANGED: {
3575                    callback.onStorageStateChanged((String) args.arg1, (String) args.arg2,
3576                            (String) args.arg3);
3577                    break;
3578                }
3579                case MSG_VOLUME_STATE_CHANGED: {
3580                    callback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3);
3581                    break;
3582                }
3583                case MSG_VOLUME_RECORD_CHANGED: {
3584                    callback.onVolumeRecordChanged((VolumeRecord) args.arg1);
3585                    break;
3586                }
3587                case MSG_VOLUME_FORGOTTEN: {
3588                    callback.onVolumeForgotten((String) args.arg1);
3589                    break;
3590                }
3591                case MSG_DISK_SCANNED: {
3592                    callback.onDiskScanned((DiskInfo) args.arg1, args.argi2);
3593                    break;
3594                }
3595                case MSG_DISK_DESTROYED: {
3596                    callback.onDiskDestroyed((DiskInfo) args.arg1);
3597                    break;
3598                }
3599            }
3600        }
3601
3602        private void notifyStorageStateChanged(String path, String oldState, String newState) {
3603            final SomeArgs args = SomeArgs.obtain();
3604            args.arg1 = path;
3605            args.arg2 = oldState;
3606            args.arg3 = newState;
3607            obtainMessage(MSG_STORAGE_STATE_CHANGED, args).sendToTarget();
3608        }
3609
3610        private void notifyVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
3611            final SomeArgs args = SomeArgs.obtain();
3612            args.arg1 = vol.clone();
3613            args.argi2 = oldState;
3614            args.argi3 = newState;
3615            obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget();
3616        }
3617
3618        private void notifyVolumeRecordChanged(VolumeRecord rec) {
3619            final SomeArgs args = SomeArgs.obtain();
3620            args.arg1 = rec.clone();
3621            obtainMessage(MSG_VOLUME_RECORD_CHANGED, args).sendToTarget();
3622        }
3623
3624        private void notifyVolumeForgotten(String fsUuid) {
3625            final SomeArgs args = SomeArgs.obtain();
3626            args.arg1 = fsUuid;
3627            obtainMessage(MSG_VOLUME_FORGOTTEN, args).sendToTarget();
3628        }
3629
3630        private void notifyDiskScanned(DiskInfo disk, int volumeCount) {
3631            final SomeArgs args = SomeArgs.obtain();
3632            args.arg1 = disk.clone();
3633            args.argi2 = volumeCount;
3634            obtainMessage(MSG_DISK_SCANNED, args).sendToTarget();
3635        }
3636
3637        private void notifyDiskDestroyed(DiskInfo disk) {
3638            final SomeArgs args = SomeArgs.obtain();
3639            args.arg1 = disk.clone();
3640            obtainMessage(MSG_DISK_DESTROYED, args).sendToTarget();
3641        }
3642    }
3643
3644    @Override
3645    protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
3646        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
3647
3648        final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ", 160);
3649        synchronized (mLock) {
3650            pw.println("Disks:");
3651            pw.increaseIndent();
3652            for (int i = 0; i < mDisks.size(); i++) {
3653                final DiskInfo disk = mDisks.valueAt(i);
3654                disk.dump(pw);
3655            }
3656            pw.decreaseIndent();
3657
3658            pw.println();
3659            pw.println("Volumes:");
3660            pw.increaseIndent();
3661            for (int i = 0; i < mVolumes.size(); i++) {
3662                final VolumeInfo vol = mVolumes.valueAt(i);
3663                if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.id)) continue;
3664                vol.dump(pw);
3665            }
3666            pw.decreaseIndent();
3667
3668            pw.println();
3669            pw.println("Records:");
3670            pw.increaseIndent();
3671            for (int i = 0; i < mRecords.size(); i++) {
3672                final VolumeRecord note = mRecords.valueAt(i);
3673                note.dump(pw);
3674            }
3675            pw.decreaseIndent();
3676
3677            pw.println();
3678            pw.println("Primary storage UUID: " + mPrimaryStorageUuid);
3679            pw.println("Force adoptable: " + mForceAdoptable);
3680            pw.println();
3681            pw.println("Local unlocked users: " + Arrays.toString(mLocalUnlockedUsers));
3682            pw.println("System unlocked users: " + Arrays.toString(mSystemUnlockedUsers));
3683        }
3684
3685        synchronized (mObbMounts) {
3686            pw.println();
3687            pw.println("mObbMounts:");
3688            pw.increaseIndent();
3689            final Iterator<Entry<IBinder, List<ObbState>>> binders = mObbMounts.entrySet()
3690                    .iterator();
3691            while (binders.hasNext()) {
3692                Entry<IBinder, List<ObbState>> e = binders.next();
3693                pw.println(e.getKey() + ":");
3694                pw.increaseIndent();
3695                final List<ObbState> obbStates = e.getValue();
3696                for (final ObbState obbState : obbStates) {
3697                    pw.println(obbState);
3698                }
3699                pw.decreaseIndent();
3700            }
3701            pw.decreaseIndent();
3702
3703            pw.println();
3704            pw.println("mObbPathToStateMap:");
3705            pw.increaseIndent();
3706            final Iterator<Entry<String, ObbState>> maps = mObbPathToStateMap.entrySet().iterator();
3707            while (maps.hasNext()) {
3708                final Entry<String, ObbState> e = maps.next();
3709                pw.print(e.getKey());
3710                pw.print(" -> ");
3711                pw.println(e.getValue());
3712            }
3713            pw.decreaseIndent();
3714        }
3715
3716        pw.println();
3717        pw.println("mConnector:");
3718        pw.increaseIndent();
3719        mConnector.dump(fd, pw, args);
3720        pw.decreaseIndent();
3721
3722        pw.println();
3723        pw.println("mCryptConnector:");
3724        pw.increaseIndent();
3725        mCryptConnector.dump(fd, pw, args);
3726        pw.decreaseIndent();
3727
3728        pw.println();
3729        pw.print("Last maintenance: ");
3730        pw.println(TimeUtils.formatForLogging(mLastMaintenance));
3731    }
3732
3733    /** {@inheritDoc} */
3734    @Override
3735    public void monitor() {
3736        if (mConnector != null) {
3737            mConnector.monitor();
3738        }
3739        if (mCryptConnector != null) {
3740            mCryptConnector.monitor();
3741        }
3742    }
3743
3744    private final class MountServiceInternalImpl extends MountServiceInternal {
3745        // Not guarded by a lock.
3746        private final CopyOnWriteArrayList<ExternalStorageMountPolicy> mPolicies =
3747                new CopyOnWriteArrayList<>();
3748
3749        @Override
3750        public void addExternalStoragePolicy(ExternalStorageMountPolicy policy) {
3751            // No locking - CopyOnWriteArrayList
3752            mPolicies.add(policy);
3753        }
3754
3755        @Override
3756        public void onExternalStoragePolicyChanged(int uid, String packageName) {
3757            final int mountMode = getExternalStorageMountMode(uid, packageName);
3758            remountUidExternalStorage(uid, mountMode);
3759        }
3760
3761        @Override
3762        public int getExternalStorageMountMode(int uid, String packageName) {
3763            // No locking - CopyOnWriteArrayList
3764            int mountMode = Integer.MAX_VALUE;
3765            for (ExternalStorageMountPolicy policy : mPolicies) {
3766                final int policyMode = policy.getMountMode(uid, packageName);
3767                if (policyMode == Zygote.MOUNT_EXTERNAL_NONE) {
3768                    return Zygote.MOUNT_EXTERNAL_NONE;
3769                }
3770                mountMode = Math.min(mountMode, policyMode);
3771            }
3772            if (mountMode == Integer.MAX_VALUE) {
3773                return Zygote.MOUNT_EXTERNAL_NONE;
3774            }
3775            return mountMode;
3776        }
3777
3778        public boolean hasExternalStorage(int uid, String packageName) {
3779            // No need to check for system uid. This avoids a deadlock between
3780            // PackageManagerService and AppOpsService.
3781            if (uid == Process.SYSTEM_UID) {
3782                return true;
3783            }
3784            // No locking - CopyOnWriteArrayList
3785            for (ExternalStorageMountPolicy policy : mPolicies) {
3786                final boolean policyHasStorage = policy.hasExternalStorage(uid, packageName);
3787                if (!policyHasStorage) {
3788                    return false;
3789                }
3790            }
3791            return true;
3792        }
3793    }
3794}
3795