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