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