MountService.java revision 6ee871e59812fea4525c50231f677c4bd10c74b8
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 ProviderInfo provider = mPms.resolveContentProvider(MediaStore.AUTHORITY, 0,
744                UserHandle.USER_OWNER);
745        if (provider != null) {
746            final IActivityManager am = ActivityManagerNative.getDefault();
747            try {
748                am.killApplicationWithAppId(provider.applicationInfo.packageName,
749                        UserHandle.getAppId(provider.applicationInfo.uid), "vold reset");
750            } catch (RemoteException e) {
751            }
752        }
753    }
754
755    private void resetIfReadyAndConnectedLocked() {
756        Slog.d(TAG, "Thinking about reset, mSystemReady=" + mSystemReady
757                + ", mDaemonConnected=" + mDaemonConnected);
758        if (mSystemReady && mDaemonConnected) {
759            killMediaProvider();
760
761            mDisks.clear();
762            mVolumes.clear();
763
764            // Create a stub volume that represents internal storage
765            final VolumeInfo internal = new VolumeInfo(VolumeInfo.ID_PRIVATE_INTERNAL,
766                    VolumeInfo.TYPE_PRIVATE, null, null);
767            internal.state = VolumeInfo.STATE_MOUNTED;
768            internal.path = Environment.getDataDirectory().getAbsolutePath();
769            mVolumes.put(internal.id, internal);
770
771            try {
772                mConnector.execute("volume", "reset");
773
774                // Tell vold about all existing and started users
775                final UserManager um = mContext.getSystemService(UserManager.class);
776                final List<UserInfo> users = um.getUsers();
777                for (UserInfo user : users) {
778                    mConnector.execute("volume", "user_added", user.id, user.serialNumber);
779                }
780                for (int userId : mStartedUsers) {
781                    mConnector.execute("volume", "user_started", userId);
782                }
783            } catch (NativeDaemonConnectorException e) {
784                Slog.w(TAG, "Failed to reset vold", e);
785            }
786        }
787    }
788
789    private void onStartUser(int userId) {
790        Slog.d(TAG, "onStartUser " + userId);
791
792        // We purposefully block here to make sure that user-specific
793        // staging area is ready so it's ready for zygote-forked apps to
794        // bind mount against.
795        try {
796            mConnector.execute("volume", "user_started", userId);
797        } catch (NativeDaemonConnectorException ignored) {
798        }
799
800        // Record user as started so newly mounted volumes kick off events
801        // correctly, then synthesize events for any already-mounted volumes.
802        synchronized (mVolumes) {
803            for (int i = 0; i < mVolumes.size(); i++) {
804                final VolumeInfo vol = mVolumes.valueAt(i);
805                if (vol.isVisibleToUser(userId) && vol.isMountedReadable()) {
806                    final StorageVolume userVol = vol.buildStorageVolume(mContext, userId, false);
807                    mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget();
808
809                    final String envState = VolumeInfo.getEnvironmentForState(vol.getState());
810                    mCallbacks.notifyStorageStateChanged(userVol.getPath(), envState, envState);
811                }
812            }
813            mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userId);
814        }
815    }
816
817    private void onCleanupUser(int userId) {
818        Slog.d(TAG, "onCleanupUser " + userId);
819
820        try {
821            mConnector.execute("volume", "user_stopped", userId);
822        } catch (NativeDaemonConnectorException ignored) {
823        }
824
825        synchronized (mVolumes) {
826            mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userId);
827        }
828    }
829
830    void runIdleMaintenance(Runnable callback) {
831        mHandler.sendMessage(mHandler.obtainMessage(H_FSTRIM, callback));
832    }
833
834    // Binder entry point for kicking off an immediate fstrim
835    @Override
836    public void runMaintenance() {
837        enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
838        runIdleMaintenance(null);
839    }
840
841    @Override
842    public long lastMaintenance() {
843        return mLastMaintenance;
844    }
845
846    /**
847     * Callback from NativeDaemonConnector
848     */
849    @Override
850    public void onDaemonConnected() {
851        mDaemonConnected = true;
852        mHandler.obtainMessage(H_DAEMON_CONNECTED).sendToTarget();
853    }
854
855    private void handleDaemonConnected() {
856        synchronized (mLock) {
857            resetIfReadyAndConnectedLocked();
858        }
859
860        /*
861         * Now that we've done our initialization, release
862         * the hounds!
863         */
864        mConnectedSignal.countDown();
865        if (mConnectedSignal.getCount() != 0) {
866            // More daemons need to connect
867            return;
868        }
869
870        // On an encrypted device we can't see system properties yet, so pull
871        // the system locale out of the mount service.
872        if ("".equals(SystemProperties.get("vold.encrypt_progress"))) {
873            copyLocaleFromMountService();
874        }
875
876        // Let package manager load internal ASECs.
877        mPms.scanAvailableAsecs();
878
879        // Notify people waiting for ASECs to be scanned that it's done.
880        mAsecsScanned.countDown();
881    }
882
883    private void copyLocaleFromMountService() {
884        String systemLocale;
885        try {
886            systemLocale = getField(StorageManager.SYSTEM_LOCALE_KEY);
887        } catch (RemoteException e) {
888            return;
889        }
890        if (TextUtils.isEmpty(systemLocale)) {
891            return;
892        }
893
894        Slog.d(TAG, "Got locale " + systemLocale + " from mount service");
895        Locale locale = Locale.forLanguageTag(systemLocale);
896        Configuration config = new Configuration();
897        config.setLocale(locale);
898        try {
899            ActivityManagerNative.getDefault().updateConfiguration(config);
900        } catch (RemoteException e) {
901            Slog.e(TAG, "Error setting system locale from mount service", e);
902        }
903
904        // Temporary workaround for http://b/17945169.
905        Slog.d(TAG, "Setting system properties to " + systemLocale + " from mount service");
906        SystemProperties.set("persist.sys.locale", locale.toLanguageTag());
907    }
908
909    /**
910     * Callback from NativeDaemonConnector
911     */
912    @Override
913    public boolean onCheckHoldWakeLock(int code) {
914        return false;
915    }
916
917    /**
918     * Callback from NativeDaemonConnector
919     */
920    @Override
921    public boolean onEvent(int code, String raw, String[] cooked) {
922        synchronized (mLock) {
923            return onEventLocked(code, raw, cooked);
924        }
925    }
926
927    private boolean onEventLocked(int code, String raw, String[] cooked) {
928        switch (code) {
929            case VoldResponseCode.DISK_CREATED: {
930                if (cooked.length != 3) break;
931                final String id = cooked[1];
932                int flags = Integer.parseInt(cooked[2]);
933                if (SystemProperties.getBoolean(StorageManager.PROP_FORCE_ADOPTABLE, false)
934                        || mForceAdoptable) {
935                    flags |= DiskInfo.FLAG_ADOPTABLE;
936                }
937                mDisks.put(id, new DiskInfo(id, flags));
938                break;
939            }
940            case VoldResponseCode.DISK_SIZE_CHANGED: {
941                if (cooked.length != 3) break;
942                final DiskInfo disk = mDisks.get(cooked[1]);
943                if (disk != null) {
944                    disk.size = Long.parseLong(cooked[2]);
945                }
946                break;
947            }
948            case VoldResponseCode.DISK_LABEL_CHANGED: {
949                final DiskInfo disk = mDisks.get(cooked[1]);
950                if (disk != null) {
951                    final StringBuilder builder = new StringBuilder();
952                    for (int i = 2; i < cooked.length; i++) {
953                        builder.append(cooked[i]).append(' ');
954                    }
955                    disk.label = builder.toString().trim();
956                }
957                break;
958            }
959            case VoldResponseCode.DISK_SCANNED: {
960                if (cooked.length != 2) break;
961                final DiskInfo disk = mDisks.get(cooked[1]);
962                if (disk != null) {
963                    onDiskScannedLocked(disk);
964                }
965                break;
966            }
967            case VoldResponseCode.DISK_SYS_PATH_CHANGED: {
968                if (cooked.length != 3) break;
969                final DiskInfo disk = mDisks.get(cooked[1]);
970                if (disk != null) {
971                    disk.sysPath = cooked[2];
972                }
973                break;
974            }
975            case VoldResponseCode.DISK_DESTROYED: {
976                if (cooked.length != 2) break;
977                final DiskInfo disk = mDisks.remove(cooked[1]);
978                if (disk != null) {
979                    mCallbacks.notifyDiskDestroyed(disk);
980                }
981                break;
982            }
983
984            case VoldResponseCode.VOLUME_CREATED: {
985                final String id = cooked[1];
986                final int type = Integer.parseInt(cooked[2]);
987                final String diskId = TextUtils.nullIfEmpty(cooked[3]);
988                final String partGuid = TextUtils.nullIfEmpty(cooked[4]);
989
990                final DiskInfo disk = mDisks.get(diskId);
991                final VolumeInfo vol = new VolumeInfo(id, type, disk, partGuid);
992                mVolumes.put(id, vol);
993                onVolumeCreatedLocked(vol);
994                break;
995            }
996            case VoldResponseCode.VOLUME_STATE_CHANGED: {
997                if (cooked.length != 3) break;
998                final VolumeInfo vol = mVolumes.get(cooked[1]);
999                if (vol != null) {
1000                    final int oldState = vol.state;
1001                    final int newState = Integer.parseInt(cooked[2]);
1002                    vol.state = newState;
1003                    onVolumeStateChangedLocked(vol, oldState, newState);
1004                }
1005                break;
1006            }
1007            case VoldResponseCode.VOLUME_FS_TYPE_CHANGED: {
1008                if (cooked.length != 3) break;
1009                final VolumeInfo vol = mVolumes.get(cooked[1]);
1010                if (vol != null) {
1011                    vol.fsType = cooked[2];
1012                }
1013                break;
1014            }
1015            case VoldResponseCode.VOLUME_FS_UUID_CHANGED: {
1016                if (cooked.length != 3) break;
1017                final VolumeInfo vol = mVolumes.get(cooked[1]);
1018                if (vol != null) {
1019                    vol.fsUuid = cooked[2];
1020                }
1021                break;
1022            }
1023            case VoldResponseCode.VOLUME_FS_LABEL_CHANGED: {
1024                final VolumeInfo vol = mVolumes.get(cooked[1]);
1025                if (vol != null) {
1026                    final StringBuilder builder = new StringBuilder();
1027                    for (int i = 2; i < cooked.length; i++) {
1028                        builder.append(cooked[i]).append(' ');
1029                    }
1030                    vol.fsLabel = builder.toString().trim();
1031                }
1032                // TODO: notify listeners that label changed
1033                break;
1034            }
1035            case VoldResponseCode.VOLUME_PATH_CHANGED: {
1036                if (cooked.length != 3) break;
1037                final VolumeInfo vol = mVolumes.get(cooked[1]);
1038                if (vol != null) {
1039                    vol.path = cooked[2];
1040                }
1041                break;
1042            }
1043            case VoldResponseCode.VOLUME_INTERNAL_PATH_CHANGED: {
1044                if (cooked.length != 3) break;
1045                final VolumeInfo vol = mVolumes.get(cooked[1]);
1046                if (vol != null) {
1047                    vol.internalPath = cooked[2];
1048                }
1049                break;
1050            }
1051            case VoldResponseCode.VOLUME_DESTROYED: {
1052                if (cooked.length != 2) break;
1053                mVolumes.remove(cooked[1]);
1054                break;
1055            }
1056
1057            case VoldResponseCode.MOVE_STATUS: {
1058                final int status = Integer.parseInt(cooked[1]);
1059                onMoveStatusLocked(status);
1060                break;
1061            }
1062            case VoldResponseCode.BENCHMARK_RESULT: {
1063                if (cooked.length != 7) break;
1064                final String path = cooked[1];
1065                final String ident = cooked[2];
1066                final long create = Long.parseLong(cooked[3]);
1067                final long drop = Long.parseLong(cooked[4]);
1068                final long run = Long.parseLong(cooked[5]);
1069                final long destroy = Long.parseLong(cooked[6]);
1070
1071                final DropBoxManager dropBox = mContext.getSystemService(DropBoxManager.class);
1072                dropBox.addText(TAG_STORAGE_BENCHMARK, scrubPath(path)
1073                        + " " + ident + " " + create + " " + run + " " + destroy);
1074
1075                final VolumeRecord rec = findRecordForPath(path);
1076                if (rec != null) {
1077                    rec.lastBenchMillis = System.currentTimeMillis();
1078                    writeSettingsLocked();
1079                }
1080
1081                break;
1082            }
1083            case VoldResponseCode.TRIM_RESULT: {
1084                if (cooked.length != 4) break;
1085                final String path = cooked[1];
1086                final long bytes = Long.parseLong(cooked[2]);
1087                final long time = Long.parseLong(cooked[3]);
1088
1089                final DropBoxManager dropBox = mContext.getSystemService(DropBoxManager.class);
1090                dropBox.addText(TAG_STORAGE_TRIM, scrubPath(path)
1091                        + " " + bytes + " " + time);
1092
1093                final VolumeRecord rec = findRecordForPath(path);
1094                if (rec != null) {
1095                    rec.lastTrimMillis = System.currentTimeMillis();
1096                    writeSettingsLocked();
1097                }
1098
1099                break;
1100            }
1101
1102            default: {
1103                Slog.d(TAG, "Unhandled vold event " + code);
1104            }
1105        }
1106
1107        return true;
1108    }
1109
1110    private void onDiskScannedLocked(DiskInfo disk) {
1111        int volumeCount = 0;
1112        for (int i = 0; i < mVolumes.size(); i++) {
1113            final VolumeInfo vol = mVolumes.valueAt(i);
1114            if (Objects.equals(disk.id, vol.getDiskId())) {
1115                volumeCount++;
1116            }
1117        }
1118
1119        final Intent intent = new Intent(DiskInfo.ACTION_DISK_SCANNED);
1120        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1121        intent.putExtra(DiskInfo.EXTRA_DISK_ID, disk.id);
1122        intent.putExtra(DiskInfo.EXTRA_VOLUME_COUNT, volumeCount);
1123        mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
1124                android.Manifest.permission.WRITE_MEDIA_STORAGE);
1125
1126        final CountDownLatch latch = mDiskScanLatches.remove(disk.id);
1127        if (latch != null) {
1128            latch.countDown();
1129        }
1130
1131        disk.volumeCount = volumeCount;
1132        mCallbacks.notifyDiskScanned(disk, volumeCount);
1133    }
1134
1135    private void onVolumeCreatedLocked(VolumeInfo vol) {
1136        if (vol.type == VolumeInfo.TYPE_EMULATED) {
1137            final StorageManager storage = mContext.getSystemService(StorageManager.class);
1138            final VolumeInfo privateVol = storage.findPrivateForEmulated(vol);
1139
1140            if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, mPrimaryStorageUuid)
1141                    && VolumeInfo.ID_PRIVATE_INTERNAL.equals(privateVol.id)) {
1142                Slog.v(TAG, "Found primary storage at " + vol);
1143                vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
1144                vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
1145                mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
1146
1147            } else if (Objects.equals(privateVol.fsUuid, mPrimaryStorageUuid)) {
1148                Slog.v(TAG, "Found primary storage at " + vol);
1149                vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
1150                vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
1151                mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
1152            }
1153
1154        } else if (vol.type == VolumeInfo.TYPE_PUBLIC) {
1155            // TODO: only look at first public partition
1156            if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, mPrimaryStorageUuid)
1157                    && vol.disk.isDefaultPrimary()) {
1158                Slog.v(TAG, "Found primary storage at " + vol);
1159                vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
1160                vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
1161            }
1162
1163            // Adoptable public disks are visible to apps, since they meet
1164            // public API requirement of being in a stable location.
1165            if (vol.disk.isAdoptable()) {
1166                vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
1167            }
1168
1169            vol.mountUserId = UserHandle.USER_OWNER;
1170            mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
1171
1172        } else if (vol.type == VolumeInfo.TYPE_PRIVATE) {
1173            mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
1174
1175        } else {
1176            Slog.d(TAG, "Skipping automatic mounting of " + vol);
1177        }
1178    }
1179
1180    private boolean isBroadcastWorthy(VolumeInfo vol) {
1181        switch (vol.getType()) {
1182            case VolumeInfo.TYPE_PRIVATE:
1183            case VolumeInfo.TYPE_PUBLIC:
1184            case VolumeInfo.TYPE_EMULATED:
1185                break;
1186            default:
1187                return false;
1188        }
1189
1190        switch (vol.getState()) {
1191            case VolumeInfo.STATE_MOUNTED:
1192            case VolumeInfo.STATE_MOUNTED_READ_ONLY:
1193            case VolumeInfo.STATE_EJECTING:
1194            case VolumeInfo.STATE_UNMOUNTED:
1195            case VolumeInfo.STATE_UNMOUNTABLE:
1196            case VolumeInfo.STATE_BAD_REMOVAL:
1197                break;
1198            default:
1199                return false;
1200        }
1201
1202        return true;
1203    }
1204
1205    private void onVolumeStateChangedLocked(VolumeInfo vol, int oldState, int newState) {
1206        // Remember that we saw this volume so we're ready to accept user
1207        // metadata, or so we can annoy them when a private volume is ejected
1208        if (vol.isMountedReadable() && !TextUtils.isEmpty(vol.fsUuid)) {
1209            VolumeRecord rec = mRecords.get(vol.fsUuid);
1210            if (rec == null) {
1211                rec = new VolumeRecord(vol.type, vol.fsUuid);
1212                rec.partGuid = vol.partGuid;
1213                rec.createdMillis = System.currentTimeMillis();
1214                if (vol.type == VolumeInfo.TYPE_PRIVATE) {
1215                    rec.nickname = vol.disk.getDescription();
1216                }
1217                mRecords.put(rec.fsUuid, rec);
1218                writeSettingsLocked();
1219            } else {
1220                // Handle upgrade case where we didn't store partition GUID
1221                if (TextUtils.isEmpty(rec.partGuid)) {
1222                    rec.partGuid = vol.partGuid;
1223                    writeSettingsLocked();
1224                }
1225            }
1226        }
1227
1228        mCallbacks.notifyVolumeStateChanged(vol, oldState, newState);
1229
1230        if (isBroadcastWorthy(vol)) {
1231            final Intent intent = new Intent(VolumeInfo.ACTION_VOLUME_STATE_CHANGED);
1232            intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.id);
1233            intent.putExtra(VolumeInfo.EXTRA_VOLUME_STATE, newState);
1234            intent.putExtra(VolumeRecord.EXTRA_FS_UUID, vol.fsUuid);
1235            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1236            mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
1237                    android.Manifest.permission.WRITE_MEDIA_STORAGE);
1238        }
1239
1240        final String oldStateEnv = VolumeInfo.getEnvironmentForState(oldState);
1241        final String newStateEnv = VolumeInfo.getEnvironmentForState(newState);
1242
1243        if (!Objects.equals(oldStateEnv, newStateEnv)) {
1244            // Kick state changed event towards all started users. Any users
1245            // started after this point will trigger additional
1246            // user-specific broadcasts.
1247            for (int userId : mStartedUsers) {
1248                if (vol.isVisibleToUser(userId)) {
1249                    final StorageVolume userVol = vol.buildStorageVolume(mContext, userId, false);
1250                    mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget();
1251
1252                    mCallbacks.notifyStorageStateChanged(userVol.getPath(), oldStateEnv,
1253                            newStateEnv);
1254                }
1255            }
1256        }
1257
1258        if (vol.type == VolumeInfo.TYPE_PUBLIC && vol.state == VolumeInfo.STATE_EJECTING) {
1259            // TODO: this should eventually be handled by new ObbVolume state changes
1260            /*
1261             * Some OBBs might have been unmounted when this volume was
1262             * unmounted, so send a message to the handler to let it know to
1263             * remove those from the list of mounted OBBS.
1264             */
1265            mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(
1266                    OBB_FLUSH_MOUNT_STATE, vol.path));
1267        }
1268    }
1269
1270    private void onMoveStatusLocked(int status) {
1271        if (mMoveCallback == null) {
1272            Slog.w(TAG, "Odd, status but no move requested");
1273            return;
1274        }
1275
1276        // TODO: estimate remaining time
1277        try {
1278            mMoveCallback.onStatusChanged(-1, status, -1);
1279        } catch (RemoteException ignored) {
1280        }
1281
1282        // We've finished copying and we're about to clean up old data, so
1283        // remember that move was successful if we get rebooted
1284        if (status == MOVE_STATUS_COPY_FINISHED) {
1285            Slog.d(TAG, "Move to " + mMoveTargetUuid + " copy phase finshed; persisting");
1286
1287            mPrimaryStorageUuid = mMoveTargetUuid;
1288            writeSettingsLocked();
1289        }
1290
1291        if (PackageManager.isMoveStatusFinished(status)) {
1292            Slog.d(TAG, "Move to " + mMoveTargetUuid + " finished with status " + status);
1293
1294            mMoveCallback = null;
1295            mMoveTargetUuid = null;
1296        }
1297    }
1298
1299    private void enforcePermission(String perm) {
1300        mContext.enforceCallingOrSelfPermission(perm, perm);
1301    }
1302
1303    private void enforceUserRestriction(String restriction) {
1304        UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
1305        if (um.hasUserRestriction(restriction, Binder.getCallingUserHandle())) {
1306            throw new SecurityException("User has restriction " + restriction);
1307        }
1308    }
1309
1310    private void enforceAdminUser() {
1311        UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
1312        final int callingUserId = UserHandle.getCallingUserId();
1313        boolean isAdmin;
1314        long token = Binder.clearCallingIdentity();
1315        try {
1316            isAdmin = um.getUserInfo(callingUserId).isAdmin();
1317        } finally {
1318            Binder.restoreCallingIdentity(token);
1319        }
1320        if (!isAdmin) {
1321            throw new SecurityException("Only admin users can adopt sd cards");
1322        }
1323    }
1324
1325    /**
1326     * Constructs a new MountService instance
1327     *
1328     * @param context  Binder context for this service
1329     */
1330    public MountService(Context context) {
1331        sSelf = this;
1332
1333        mContext = context;
1334        mCallbacks = new Callbacks(FgThread.get().getLooper());
1335
1336        // XXX: This will go away soon in favor of IMountServiceObserver
1337        mPms = (PackageManagerService) ServiceManager.getService("package");
1338
1339        HandlerThread hthread = new HandlerThread(TAG);
1340        hthread.start();
1341        mHandler = new MountServiceHandler(hthread.getLooper());
1342
1343        // Add OBB Action Handler to MountService thread.
1344        mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper());
1345
1346        // Initialize the last-fstrim tracking if necessary
1347        File dataDir = Environment.getDataDirectory();
1348        File systemDir = new File(dataDir, "system");
1349        mLastMaintenanceFile = new File(systemDir, LAST_FSTRIM_FILE);
1350        if (!mLastMaintenanceFile.exists()) {
1351            // Not setting mLastMaintenance here means that we will force an
1352            // fstrim during reboot following the OTA that installs this code.
1353            try {
1354                (new FileOutputStream(mLastMaintenanceFile)).close();
1355            } catch (IOException e) {
1356                Slog.e(TAG, "Unable to create fstrim record " + mLastMaintenanceFile.getPath());
1357            }
1358        } else {
1359            mLastMaintenance = mLastMaintenanceFile.lastModified();
1360        }
1361
1362        mSettingsFile = new AtomicFile(
1363                new File(Environment.getSystemSecureDirectory(), "storage.xml"));
1364
1365        synchronized (mLock) {
1366            readSettingsLocked();
1367        }
1368
1369        LocalServices.addService(MountServiceInternal.class, mMountServiceInternal);
1370
1371        /*
1372         * Create the connection to vold with a maximum queue of twice the
1373         * amount of containers we'd ever expect to have. This keeps an
1374         * "asec list" from blocking a thread repeatedly.
1375         */
1376
1377        mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25,
1378                null);
1379        mConnector.setDebug(true);
1380
1381        Thread thread = new Thread(mConnector, VOLD_TAG);
1382        thread.start();
1383
1384        // Reuse parameters from first connector since they are tested and safe
1385        mCryptConnector = new NativeDaemonConnector(this, "cryptd",
1386                MAX_CONTAINERS * 2, CRYPTD_TAG, 25, null);
1387        mCryptConnector.setDebug(true);
1388
1389        Thread crypt_thread = new Thread(mCryptConnector, CRYPTD_TAG);
1390        crypt_thread.start();
1391
1392        final IntentFilter userFilter = new IntentFilter();
1393        userFilter.addAction(Intent.ACTION_USER_ADDED);
1394        userFilter.addAction(Intent.ACTION_USER_REMOVED);
1395        mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);
1396
1397        // Add ourself to the Watchdog monitors if enabled.
1398        if (WATCHDOG_ENABLE) {
1399            Watchdog.getInstance().addMonitor(this);
1400        }
1401    }
1402
1403    private void systemReady() {
1404        mSystemReady = true;
1405        mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget();
1406    }
1407
1408    private String getDefaultPrimaryStorageUuid() {
1409        if (SystemProperties.getBoolean(StorageManager.PROP_PRIMARY_PHYSICAL, false)) {
1410            return StorageManager.UUID_PRIMARY_PHYSICAL;
1411        } else {
1412            return StorageManager.UUID_PRIVATE_INTERNAL;
1413        }
1414    }
1415
1416    private void readSettingsLocked() {
1417        mRecords.clear();
1418        mPrimaryStorageUuid = getDefaultPrimaryStorageUuid();
1419        mForceAdoptable = false;
1420
1421        FileInputStream fis = null;
1422        try {
1423            fis = mSettingsFile.openRead();
1424            final XmlPullParser in = Xml.newPullParser();
1425            in.setInput(fis, StandardCharsets.UTF_8.name());
1426
1427            int type;
1428            while ((type = in.next()) != END_DOCUMENT) {
1429                if (type == START_TAG) {
1430                    final String tag = in.getName();
1431                    if (TAG_VOLUMES.equals(tag)) {
1432                        final int version = readIntAttribute(in, ATTR_VERSION, VERSION_INIT);
1433                        final boolean primaryPhysical = SystemProperties.getBoolean(
1434                                StorageManager.PROP_PRIMARY_PHYSICAL, false);
1435                        final boolean validAttr = (version >= VERSION_FIX_PRIMARY)
1436                                || (version >= VERSION_ADD_PRIMARY && !primaryPhysical);
1437                        if (validAttr) {
1438                            mPrimaryStorageUuid = readStringAttribute(in,
1439                                    ATTR_PRIMARY_STORAGE_UUID);
1440                        }
1441                        mForceAdoptable = readBooleanAttribute(in, ATTR_FORCE_ADOPTABLE, false);
1442
1443                    } else if (TAG_VOLUME.equals(tag)) {
1444                        final VolumeRecord rec = readVolumeRecord(in);
1445                        mRecords.put(rec.fsUuid, rec);
1446                    }
1447                }
1448            }
1449        } catch (FileNotFoundException e) {
1450            // Missing metadata is okay, probably first boot
1451        } catch (IOException e) {
1452            Slog.wtf(TAG, "Failed reading metadata", e);
1453        } catch (XmlPullParserException e) {
1454            Slog.wtf(TAG, "Failed reading metadata", e);
1455        } finally {
1456            IoUtils.closeQuietly(fis);
1457        }
1458    }
1459
1460    private void writeSettingsLocked() {
1461        FileOutputStream fos = null;
1462        try {
1463            fos = mSettingsFile.startWrite();
1464
1465            XmlSerializer out = new FastXmlSerializer();
1466            out.setOutput(fos, StandardCharsets.UTF_8.name());
1467            out.startDocument(null, true);
1468            out.startTag(null, TAG_VOLUMES);
1469            writeIntAttribute(out, ATTR_VERSION, VERSION_FIX_PRIMARY);
1470            writeStringAttribute(out, ATTR_PRIMARY_STORAGE_UUID, mPrimaryStorageUuid);
1471            writeBooleanAttribute(out, ATTR_FORCE_ADOPTABLE, mForceAdoptable);
1472            final int size = mRecords.size();
1473            for (int i = 0; i < size; i++) {
1474                final VolumeRecord rec = mRecords.valueAt(i);
1475                writeVolumeRecord(out, rec);
1476            }
1477            out.endTag(null, TAG_VOLUMES);
1478            out.endDocument();
1479
1480            mSettingsFile.finishWrite(fos);
1481        } catch (IOException e) {
1482            if (fos != null) {
1483                mSettingsFile.failWrite(fos);
1484            }
1485        }
1486    }
1487
1488    public static VolumeRecord readVolumeRecord(XmlPullParser in) throws IOException {
1489        final int type = readIntAttribute(in, ATTR_TYPE);
1490        final String fsUuid = readStringAttribute(in, ATTR_FS_UUID);
1491        final VolumeRecord meta = new VolumeRecord(type, fsUuid);
1492        meta.partGuid = readStringAttribute(in, ATTR_PART_GUID);
1493        meta.nickname = readStringAttribute(in, ATTR_NICKNAME);
1494        meta.userFlags = readIntAttribute(in, ATTR_USER_FLAGS);
1495        meta.createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS);
1496        meta.lastTrimMillis = readLongAttribute(in, ATTR_LAST_TRIM_MILLIS);
1497        meta.lastBenchMillis = readLongAttribute(in, ATTR_LAST_BENCH_MILLIS);
1498        return meta;
1499    }
1500
1501    public static void writeVolumeRecord(XmlSerializer out, VolumeRecord rec) throws IOException {
1502        out.startTag(null, TAG_VOLUME);
1503        writeIntAttribute(out, ATTR_TYPE, rec.type);
1504        writeStringAttribute(out, ATTR_FS_UUID, rec.fsUuid);
1505        writeStringAttribute(out, ATTR_PART_GUID, rec.partGuid);
1506        writeStringAttribute(out, ATTR_NICKNAME, rec.nickname);
1507        writeIntAttribute(out, ATTR_USER_FLAGS, rec.userFlags);
1508        writeLongAttribute(out, ATTR_CREATED_MILLIS, rec.createdMillis);
1509        writeLongAttribute(out, ATTR_LAST_TRIM_MILLIS, rec.lastTrimMillis);
1510        writeLongAttribute(out, ATTR_LAST_BENCH_MILLIS, rec.lastBenchMillis);
1511        out.endTag(null, TAG_VOLUME);
1512    }
1513
1514    /**
1515     * Exposed API calls below here
1516     */
1517
1518    @Override
1519    public void registerListener(IMountServiceListener listener) {
1520        mCallbacks.register(listener);
1521    }
1522
1523    @Override
1524    public void unregisterListener(IMountServiceListener listener) {
1525        mCallbacks.unregister(listener);
1526    }
1527
1528    @Override
1529    public void shutdown(final IMountShutdownObserver observer) {
1530        enforcePermission(android.Manifest.permission.SHUTDOWN);
1531
1532        Slog.i(TAG, "Shutting down");
1533        mHandler.obtainMessage(H_SHUTDOWN, observer).sendToTarget();
1534    }
1535
1536    @Override
1537    public boolean isUsbMassStorageConnected() {
1538        throw new UnsupportedOperationException();
1539    }
1540
1541    @Override
1542    public void setUsbMassStorageEnabled(boolean enable) {
1543        throw new UnsupportedOperationException();
1544    }
1545
1546    @Override
1547    public boolean isUsbMassStorageEnabled() {
1548        throw new UnsupportedOperationException();
1549    }
1550
1551    @Override
1552    public String getVolumeState(String mountPoint) {
1553        throw new UnsupportedOperationException();
1554    }
1555
1556    @Override
1557    public boolean isExternalStorageEmulated() {
1558        throw new UnsupportedOperationException();
1559    }
1560
1561    @Override
1562    public int mountVolume(String path) {
1563        mount(findVolumeIdForPathOrThrow(path));
1564        return 0;
1565    }
1566
1567    @Override
1568    public void unmountVolume(String path, boolean force, boolean removeEncryption) {
1569        unmount(findVolumeIdForPathOrThrow(path));
1570    }
1571
1572    @Override
1573    public int formatVolume(String path) {
1574        format(findVolumeIdForPathOrThrow(path));
1575        return 0;
1576    }
1577
1578    @Override
1579    public void mount(String volId) {
1580        enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1581        waitForReady();
1582
1583        final VolumeInfo vol = findVolumeByIdOrThrow(volId);
1584        if (vol.type == VolumeInfo.TYPE_PUBLIC || vol.type == VolumeInfo.TYPE_PRIVATE) {
1585            enforceUserRestriction(UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA);
1586        }
1587        try {
1588            mConnector.execute("volume", "mount", vol.id, vol.mountFlags, vol.mountUserId);
1589        } catch (NativeDaemonConnectorException e) {
1590            throw e.rethrowAsParcelableException();
1591        }
1592    }
1593
1594    @Override
1595    public void unmount(String volId) {
1596        enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1597        waitForReady();
1598
1599        final VolumeInfo vol = findVolumeByIdOrThrow(volId);
1600
1601        // TODO: expand PMS to know about multiple volumes
1602        if (vol.isPrimaryPhysical()) {
1603            final long ident = Binder.clearCallingIdentity();
1604            try {
1605                synchronized (mUnmountLock) {
1606                    mUnmountSignal = new CountDownLatch(1);
1607                    mPms.updateExternalMediaStatus(false, true);
1608                    waitForLatch(mUnmountSignal, "mUnmountSignal");
1609                    mUnmountSignal = null;
1610                }
1611            } finally {
1612                Binder.restoreCallingIdentity(ident);
1613            }
1614        }
1615
1616        try {
1617            mConnector.execute("volume", "unmount", vol.id);
1618        } catch (NativeDaemonConnectorException e) {
1619            throw e.rethrowAsParcelableException();
1620        }
1621    }
1622
1623    @Override
1624    public void format(String volId) {
1625        enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
1626        waitForReady();
1627
1628        final VolumeInfo vol = findVolumeByIdOrThrow(volId);
1629        try {
1630            mConnector.execute("volume", "format", vol.id, "auto");
1631        } catch (NativeDaemonConnectorException e) {
1632            throw e.rethrowAsParcelableException();
1633        }
1634    }
1635
1636    @Override
1637    public long benchmark(String volId) {
1638        enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
1639        waitForReady();
1640
1641        try {
1642            // TODO: make benchmark async so we don't block other commands
1643            final NativeDaemonEvent res = mConnector.execute(3 * DateUtils.MINUTE_IN_MILLIS,
1644                    "volume", "benchmark", volId);
1645            return Long.parseLong(res.getMessage());
1646        } catch (NativeDaemonTimeoutException e) {
1647            return Long.MAX_VALUE;
1648        } catch (NativeDaemonConnectorException e) {
1649            throw e.rethrowAsParcelableException();
1650        }
1651    }
1652
1653    @Override
1654    public void partitionPublic(String diskId) {
1655        enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
1656        waitForReady();
1657
1658        final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
1659        try {
1660            mConnector.execute("volume", "partition", diskId, "public");
1661            waitForLatch(latch, "partitionPublic", 3 * DateUtils.MINUTE_IN_MILLIS);
1662        } catch (NativeDaemonConnectorException e) {
1663            throw e.rethrowAsParcelableException();
1664        } catch (TimeoutException e) {
1665            throw new IllegalStateException(e);
1666        }
1667    }
1668
1669    @Override
1670    public void partitionPrivate(String diskId) {
1671        enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
1672        enforceAdminUser();
1673        waitForReady();
1674
1675        final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
1676        try {
1677            mConnector.execute("volume", "partition", diskId, "private");
1678            waitForLatch(latch, "partitionPrivate", 3 * DateUtils.MINUTE_IN_MILLIS);
1679        } catch (NativeDaemonConnectorException e) {
1680            throw e.rethrowAsParcelableException();
1681        } catch (TimeoutException e) {
1682            throw new IllegalStateException(e);
1683        }
1684    }
1685
1686    @Override
1687    public void partitionMixed(String diskId, int ratio) {
1688        enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
1689        enforceAdminUser();
1690        waitForReady();
1691
1692        final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
1693        try {
1694            mConnector.execute("volume", "partition", diskId, "mixed", ratio);
1695            waitForLatch(latch, "partitionMixed", 3 * DateUtils.MINUTE_IN_MILLIS);
1696        } catch (NativeDaemonConnectorException e) {
1697            throw e.rethrowAsParcelableException();
1698        } catch (TimeoutException e) {
1699            throw new IllegalStateException(e);
1700        }
1701    }
1702
1703    @Override
1704    public void setVolumeNickname(String fsUuid, String nickname) {
1705        enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1706        waitForReady();
1707
1708        Preconditions.checkNotNull(fsUuid);
1709        synchronized (mLock) {
1710            final VolumeRecord rec = mRecords.get(fsUuid);
1711            rec.nickname = nickname;
1712            mCallbacks.notifyVolumeRecordChanged(rec);
1713            writeSettingsLocked();
1714        }
1715    }
1716
1717    @Override
1718    public void setVolumeUserFlags(String fsUuid, int flags, int mask) {
1719        enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1720        waitForReady();
1721
1722        Preconditions.checkNotNull(fsUuid);
1723        synchronized (mLock) {
1724            final VolumeRecord rec = mRecords.get(fsUuid);
1725            rec.userFlags = (rec.userFlags & ~mask) | (flags & mask);
1726            mCallbacks.notifyVolumeRecordChanged(rec);
1727            writeSettingsLocked();
1728        }
1729    }
1730
1731    @Override
1732    public void forgetVolume(String fsUuid) {
1733        enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1734        waitForReady();
1735
1736        Preconditions.checkNotNull(fsUuid);
1737        synchronized (mLock) {
1738            final VolumeRecord rec = mRecords.remove(fsUuid);
1739            if (rec != null && !TextUtils.isEmpty(rec.partGuid)) {
1740                forgetPartition(rec.partGuid);
1741            }
1742            mCallbacks.notifyVolumeForgotten(fsUuid);
1743
1744            // If this had been primary storage, revert back to internal and
1745            // reset vold so we bind into new volume into place.
1746            if (Objects.equals(mPrimaryStorageUuid, fsUuid)) {
1747                mPrimaryStorageUuid = getDefaultPrimaryStorageUuid();
1748                resetIfReadyAndConnectedLocked();
1749            }
1750
1751            writeSettingsLocked();
1752        }
1753    }
1754
1755    @Override
1756    public void forgetAllVolumes() {
1757        enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1758        waitForReady();
1759
1760        synchronized (mLock) {
1761            for (int i = 0; i < mRecords.size(); i++) {
1762                final String fsUuid = mRecords.keyAt(i);
1763                final VolumeRecord rec = mRecords.valueAt(i);
1764                if (!TextUtils.isEmpty(rec.partGuid)) {
1765                    forgetPartition(rec.partGuid);
1766                }
1767                mCallbacks.notifyVolumeForgotten(fsUuid);
1768            }
1769            mRecords.clear();
1770
1771            if (!Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, mPrimaryStorageUuid)) {
1772                mPrimaryStorageUuid = getDefaultPrimaryStorageUuid();
1773            }
1774
1775            writeSettingsLocked();
1776            resetIfReadyAndConnectedLocked();
1777        }
1778    }
1779
1780    private void forgetPartition(String partGuid) {
1781        try {
1782            mConnector.execute("volume", "forget_partition", partGuid);
1783        } catch (NativeDaemonConnectorException e) {
1784            Slog.w(TAG, "Failed to forget key for " + partGuid + ": " + e);
1785        }
1786    }
1787
1788    private void remountUidExternalStorage(int uid, int mode) {
1789        waitForReady();
1790
1791        String modeName = "none";
1792        switch (mode) {
1793            case Zygote.MOUNT_EXTERNAL_DEFAULT: {
1794                modeName = "default";
1795            } break;
1796
1797            case Zygote.MOUNT_EXTERNAL_READ: {
1798                modeName = "read";
1799            } break;
1800
1801            case Zygote.MOUNT_EXTERNAL_WRITE: {
1802                modeName = "write";
1803            } break;
1804        }
1805
1806        try {
1807            mConnector.execute("volume", "remount_uid", uid, modeName);
1808        } catch (NativeDaemonConnectorException e) {
1809            Slog.w(TAG, "Failed to remount UID " + uid + " as " + modeName + ": " + e);
1810        }
1811    }
1812
1813    @Override
1814    public void setDebugFlags(int flags, int mask) {
1815        enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1816        waitForReady();
1817
1818        synchronized (mLock) {
1819            if ((mask & StorageManager.DEBUG_FORCE_ADOPTABLE) != 0) {
1820                mForceAdoptable = (flags & StorageManager.DEBUG_FORCE_ADOPTABLE) != 0;
1821            }
1822
1823            writeSettingsLocked();
1824            resetIfReadyAndConnectedLocked();
1825        }
1826    }
1827
1828    @Override
1829    public String getPrimaryStorageUuid() {
1830        enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1831        waitForReady();
1832
1833        synchronized (mLock) {
1834            return mPrimaryStorageUuid;
1835        }
1836    }
1837
1838    @Override
1839    public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) {
1840        enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1841        waitForReady();
1842
1843        synchronized (mLock) {
1844            if (Objects.equals(mPrimaryStorageUuid, volumeUuid)) {
1845                throw new IllegalArgumentException("Primary storage already at " + volumeUuid);
1846            }
1847
1848            if (mMoveCallback != null) {
1849                throw new IllegalStateException("Move already in progress");
1850            }
1851            mMoveCallback = callback;
1852            mMoveTargetUuid = volumeUuid;
1853
1854            // When moving to/from primary physical volume, we probably just nuked
1855            // the current storage location, so we have nothing to move.
1856            if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, mPrimaryStorageUuid)
1857                    || Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
1858                Slog.d(TAG, "Skipping move to/from primary physical");
1859                onMoveStatusLocked(MOVE_STATUS_COPY_FINISHED);
1860                onMoveStatusLocked(PackageManager.MOVE_SUCCEEDED);
1861                resetIfReadyAndConnectedLocked();
1862
1863            } else {
1864                final VolumeInfo from = findStorageForUuid(mPrimaryStorageUuid);
1865                final VolumeInfo to = findStorageForUuid(volumeUuid);
1866
1867                if (from == null) {
1868                    Slog.w(TAG, "Failing move due to missing from volume " + mPrimaryStorageUuid);
1869                    onMoveStatusLocked(PackageManager.MOVE_FAILED_INTERNAL_ERROR);
1870                    return;
1871                } else if (to == null) {
1872                    Slog.w(TAG, "Failing move due to missing to volume " + volumeUuid);
1873                    onMoveStatusLocked(PackageManager.MOVE_FAILED_INTERNAL_ERROR);
1874                    return;
1875                }
1876
1877                try {
1878                    mConnector.execute("volume", "move_storage", from.id, to.id);
1879                } catch (NativeDaemonConnectorException e) {
1880                    throw e.rethrowAsParcelableException();
1881                }
1882            }
1883        }
1884    }
1885
1886    @Override
1887    public int[] getStorageUsers(String path) {
1888        enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1889        waitForReady();
1890        try {
1891            final String[] r = NativeDaemonEvent.filterMessageList(
1892                    mConnector.executeForList("storage", "users", path),
1893                    VoldResponseCode.StorageUsersListResult);
1894
1895            // FMT: <pid> <process name>
1896            int[] data = new int[r.length];
1897            for (int i = 0; i < r.length; i++) {
1898                String[] tok = r[i].split(" ");
1899                try {
1900                    data[i] = Integer.parseInt(tok[0]);
1901                } catch (NumberFormatException nfe) {
1902                    Slog.e(TAG, String.format("Error parsing pid %s", tok[0]));
1903                    return new int[0];
1904                }
1905            }
1906            return data;
1907        } catch (NativeDaemonConnectorException e) {
1908            Slog.e(TAG, "Failed to retrieve storage users list", e);
1909            return new int[0];
1910        }
1911    }
1912
1913    private void warnOnNotMounted() {
1914        synchronized (mLock) {
1915            for (int i = 0; i < mVolumes.size(); i++) {
1916                final VolumeInfo vol = mVolumes.valueAt(i);
1917                if (vol.isPrimary() && vol.isMountedWritable()) {
1918                    // Cool beans, we have a mounted primary volume
1919                    return;
1920                }
1921            }
1922        }
1923
1924        Slog.w(TAG, "No primary storage mounted!");
1925    }
1926
1927    public String[] getSecureContainerList() {
1928        enforcePermission(android.Manifest.permission.ASEC_ACCESS);
1929        waitForReady();
1930        warnOnNotMounted();
1931
1932        try {
1933            return NativeDaemonEvent.filterMessageList(
1934                    mConnector.executeForList("asec", "list"), VoldResponseCode.AsecListResult);
1935        } catch (NativeDaemonConnectorException e) {
1936            return new String[0];
1937        }
1938    }
1939
1940    public int createSecureContainer(String id, int sizeMb, String fstype, String key,
1941            int ownerUid, boolean external) {
1942        enforcePermission(android.Manifest.permission.ASEC_CREATE);
1943        waitForReady();
1944        warnOnNotMounted();
1945
1946        int rc = StorageResultCode.OperationSucceeded;
1947        try {
1948            mConnector.execute("asec", "create", id, sizeMb, fstype, new SensitiveArg(key),
1949                    ownerUid, external ? "1" : "0");
1950        } catch (NativeDaemonConnectorException e) {
1951            rc = StorageResultCode.OperationFailedInternalError;
1952        }
1953
1954        if (rc == StorageResultCode.OperationSucceeded) {
1955            synchronized (mAsecMountSet) {
1956                mAsecMountSet.add(id);
1957            }
1958        }
1959        return rc;
1960    }
1961
1962    @Override
1963    public int resizeSecureContainer(String id, int sizeMb, String key) {
1964        enforcePermission(android.Manifest.permission.ASEC_CREATE);
1965        waitForReady();
1966        warnOnNotMounted();
1967
1968        int rc = StorageResultCode.OperationSucceeded;
1969        try {
1970            mConnector.execute("asec", "resize", id, sizeMb, new SensitiveArg(key));
1971        } catch (NativeDaemonConnectorException e) {
1972            rc = StorageResultCode.OperationFailedInternalError;
1973        }
1974        return rc;
1975    }
1976
1977    public int finalizeSecureContainer(String id) {
1978        enforcePermission(android.Manifest.permission.ASEC_CREATE);
1979        warnOnNotMounted();
1980
1981        int rc = StorageResultCode.OperationSucceeded;
1982        try {
1983            mConnector.execute("asec", "finalize", id);
1984            /*
1985             * Finalization does a remount, so no need
1986             * to update mAsecMountSet
1987             */
1988        } catch (NativeDaemonConnectorException e) {
1989            rc = StorageResultCode.OperationFailedInternalError;
1990        }
1991        return rc;
1992    }
1993
1994    public int fixPermissionsSecureContainer(String id, int gid, String filename) {
1995        enforcePermission(android.Manifest.permission.ASEC_CREATE);
1996        warnOnNotMounted();
1997
1998        int rc = StorageResultCode.OperationSucceeded;
1999        try {
2000            mConnector.execute("asec", "fixperms", id, gid, filename);
2001            /*
2002             * Fix permissions does a remount, so no need to update
2003             * mAsecMountSet
2004             */
2005        } catch (NativeDaemonConnectorException e) {
2006            rc = StorageResultCode.OperationFailedInternalError;
2007        }
2008        return rc;
2009    }
2010
2011    public int destroySecureContainer(String id, boolean force) {
2012        enforcePermission(android.Manifest.permission.ASEC_DESTROY);
2013        waitForReady();
2014        warnOnNotMounted();
2015
2016        /*
2017         * Force a GC to make sure AssetManagers in other threads of the
2018         * system_server are cleaned up. We have to do this since AssetManager
2019         * instances are kept as a WeakReference and it's possible we have files
2020         * open on the external storage.
2021         */
2022        Runtime.getRuntime().gc();
2023
2024        int rc = StorageResultCode.OperationSucceeded;
2025        try {
2026            final Command cmd = new Command("asec", "destroy", id);
2027            if (force) {
2028                cmd.appendArg("force");
2029            }
2030            mConnector.execute(cmd);
2031        } catch (NativeDaemonConnectorException e) {
2032            int code = e.getCode();
2033            if (code == VoldResponseCode.OpFailedStorageBusy) {
2034                rc = StorageResultCode.OperationFailedStorageBusy;
2035            } else {
2036                rc = StorageResultCode.OperationFailedInternalError;
2037            }
2038        }
2039
2040        if (rc == StorageResultCode.OperationSucceeded) {
2041            synchronized (mAsecMountSet) {
2042                if (mAsecMountSet.contains(id)) {
2043                    mAsecMountSet.remove(id);
2044                }
2045            }
2046        }
2047
2048        return rc;
2049    }
2050
2051    public int mountSecureContainer(String id, String key, int ownerUid, boolean readOnly) {
2052        enforcePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
2053        waitForReady();
2054        warnOnNotMounted();
2055
2056        synchronized (mAsecMountSet) {
2057            if (mAsecMountSet.contains(id)) {
2058                return StorageResultCode.OperationFailedStorageMounted;
2059            }
2060        }
2061
2062        int rc = StorageResultCode.OperationSucceeded;
2063        try {
2064            mConnector.execute("asec", "mount", id, new SensitiveArg(key), ownerUid,
2065                    readOnly ? "ro" : "rw");
2066        } catch (NativeDaemonConnectorException e) {
2067            int code = e.getCode();
2068            if (code != VoldResponseCode.OpFailedStorageBusy) {
2069                rc = StorageResultCode.OperationFailedInternalError;
2070            }
2071        }
2072
2073        if (rc == StorageResultCode.OperationSucceeded) {
2074            synchronized (mAsecMountSet) {
2075                mAsecMountSet.add(id);
2076            }
2077        }
2078        return rc;
2079    }
2080
2081    public int unmountSecureContainer(String id, boolean force) {
2082        enforcePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
2083        waitForReady();
2084        warnOnNotMounted();
2085
2086        synchronized (mAsecMountSet) {
2087            if (!mAsecMountSet.contains(id)) {
2088                return StorageResultCode.OperationFailedStorageNotMounted;
2089            }
2090         }
2091
2092        /*
2093         * Force a GC to make sure AssetManagers in other threads of the
2094         * system_server are cleaned up. We have to do this since AssetManager
2095         * instances are kept as a WeakReference and it's possible we have files
2096         * open on the external storage.
2097         */
2098        Runtime.getRuntime().gc();
2099
2100        int rc = StorageResultCode.OperationSucceeded;
2101        try {
2102            final Command cmd = new Command("asec", "unmount", id);
2103            if (force) {
2104                cmd.appendArg("force");
2105            }
2106            mConnector.execute(cmd);
2107        } catch (NativeDaemonConnectorException e) {
2108            int code = e.getCode();
2109            if (code == VoldResponseCode.OpFailedStorageBusy) {
2110                rc = StorageResultCode.OperationFailedStorageBusy;
2111            } else {
2112                rc = StorageResultCode.OperationFailedInternalError;
2113            }
2114        }
2115
2116        if (rc == StorageResultCode.OperationSucceeded) {
2117            synchronized (mAsecMountSet) {
2118                mAsecMountSet.remove(id);
2119            }
2120        }
2121        return rc;
2122    }
2123
2124    public boolean isSecureContainerMounted(String id) {
2125        enforcePermission(android.Manifest.permission.ASEC_ACCESS);
2126        waitForReady();
2127        warnOnNotMounted();
2128
2129        synchronized (mAsecMountSet) {
2130            return mAsecMountSet.contains(id);
2131        }
2132    }
2133
2134    public int renameSecureContainer(String oldId, String newId) {
2135        enforcePermission(android.Manifest.permission.ASEC_RENAME);
2136        waitForReady();
2137        warnOnNotMounted();
2138
2139        synchronized (mAsecMountSet) {
2140            /*
2141             * Because a mounted container has active internal state which cannot be
2142             * changed while active, we must ensure both ids are not currently mounted.
2143             */
2144            if (mAsecMountSet.contains(oldId) || mAsecMountSet.contains(newId)) {
2145                return StorageResultCode.OperationFailedStorageMounted;
2146            }
2147        }
2148
2149        int rc = StorageResultCode.OperationSucceeded;
2150        try {
2151            mConnector.execute("asec", "rename", oldId, newId);
2152        } catch (NativeDaemonConnectorException e) {
2153            rc = StorageResultCode.OperationFailedInternalError;
2154        }
2155
2156        return rc;
2157    }
2158
2159    public String getSecureContainerPath(String id) {
2160        enforcePermission(android.Manifest.permission.ASEC_ACCESS);
2161        waitForReady();
2162        warnOnNotMounted();
2163
2164        final NativeDaemonEvent event;
2165        try {
2166            event = mConnector.execute("asec", "path", id);
2167            event.checkCode(VoldResponseCode.AsecPathResult);
2168            return event.getMessage();
2169        } catch (NativeDaemonConnectorException e) {
2170            int code = e.getCode();
2171            if (code == VoldResponseCode.OpFailedStorageNotFound) {
2172                Slog.i(TAG, String.format("Container '%s' not found", id));
2173                return null;
2174            } else {
2175                throw new IllegalStateException(String.format("Unexpected response code %d", code));
2176            }
2177        }
2178    }
2179
2180    public String getSecureContainerFilesystemPath(String id) {
2181        enforcePermission(android.Manifest.permission.ASEC_ACCESS);
2182        waitForReady();
2183        warnOnNotMounted();
2184
2185        final NativeDaemonEvent event;
2186        try {
2187            event = mConnector.execute("asec", "fspath", id);
2188            event.checkCode(VoldResponseCode.AsecPathResult);
2189            return event.getMessage();
2190        } catch (NativeDaemonConnectorException e) {
2191            int code = e.getCode();
2192            if (code == VoldResponseCode.OpFailedStorageNotFound) {
2193                Slog.i(TAG, String.format("Container '%s' not found", id));
2194                return null;
2195            } else {
2196                throw new IllegalStateException(String.format("Unexpected response code %d", code));
2197            }
2198        }
2199    }
2200
2201    @Override
2202    public void finishMediaUpdate() {
2203        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
2204            throw new SecurityException("no permission to call finishMediaUpdate()");
2205        }
2206        if (mUnmountSignal != null) {
2207            mUnmountSignal.countDown();
2208        } else {
2209            Slog.w(TAG, "Odd, nobody asked to unmount?");
2210        }
2211    }
2212
2213    private boolean isUidOwnerOfPackageOrSystem(String packageName, int callerUid) {
2214        if (callerUid == android.os.Process.SYSTEM_UID) {
2215            return true;
2216        }
2217
2218        if (packageName == null) {
2219            return false;
2220        }
2221
2222        final int packageUid = mPms.getPackageUid(packageName, UserHandle.getUserId(callerUid));
2223
2224        if (DEBUG_OBB) {
2225            Slog.d(TAG, "packageName = " + packageName + ", packageUid = " +
2226                    packageUid + ", callerUid = " + callerUid);
2227        }
2228
2229        return callerUid == packageUid;
2230    }
2231
2232    public String getMountedObbPath(String rawPath) {
2233        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
2234
2235        waitForReady();
2236        warnOnNotMounted();
2237
2238        final ObbState state;
2239        synchronized (mObbMounts) {
2240            state = mObbPathToStateMap.get(rawPath);
2241        }
2242        if (state == null) {
2243            Slog.w(TAG, "Failed to find OBB mounted at " + rawPath);
2244            return null;
2245        }
2246
2247        final NativeDaemonEvent event;
2248        try {
2249            event = mConnector.execute("obb", "path", state.voldPath);
2250            event.checkCode(VoldResponseCode.AsecPathResult);
2251            return event.getMessage();
2252        } catch (NativeDaemonConnectorException e) {
2253            int code = e.getCode();
2254            if (code == VoldResponseCode.OpFailedStorageNotFound) {
2255                return null;
2256            } else {
2257                throw new IllegalStateException(String.format("Unexpected response code %d", code));
2258            }
2259        }
2260    }
2261
2262    @Override
2263    public boolean isObbMounted(String rawPath) {
2264        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
2265        synchronized (mObbMounts) {
2266            return mObbPathToStateMap.containsKey(rawPath);
2267        }
2268    }
2269
2270    @Override
2271    public void mountObb(
2272            String rawPath, String canonicalPath, String key, IObbActionListener token, int nonce) {
2273        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
2274        Preconditions.checkNotNull(canonicalPath, "canonicalPath cannot be null");
2275        Preconditions.checkNotNull(token, "token cannot be null");
2276
2277        final int callingUid = Binder.getCallingUid();
2278        final ObbState obbState = new ObbState(rawPath, canonicalPath, callingUid, token, nonce);
2279        final ObbAction action = new MountObbAction(obbState, key, callingUid);
2280        mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
2281
2282        if (DEBUG_OBB)
2283            Slog.i(TAG, "Send to OBB handler: " + action.toString());
2284    }
2285
2286    @Override
2287    public void unmountObb(String rawPath, boolean force, IObbActionListener token, int nonce) {
2288        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
2289
2290        final ObbState existingState;
2291        synchronized (mObbMounts) {
2292            existingState = mObbPathToStateMap.get(rawPath);
2293        }
2294
2295        if (existingState != null) {
2296            // TODO: separate state object from request data
2297            final int callingUid = Binder.getCallingUid();
2298            final ObbState newState = new ObbState(
2299                    rawPath, existingState.canonicalPath, callingUid, token, nonce);
2300            final ObbAction action = new UnmountObbAction(newState, force);
2301            mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
2302
2303            if (DEBUG_OBB)
2304                Slog.i(TAG, "Send to OBB handler: " + action.toString());
2305        } else {
2306            Slog.w(TAG, "Unknown OBB mount at " + rawPath);
2307        }
2308    }
2309
2310    @Override
2311    public int getEncryptionState() {
2312        mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2313                "no permission to access the crypt keeper");
2314
2315        waitForReady();
2316
2317        final NativeDaemonEvent event;
2318        try {
2319            event = mCryptConnector.execute("cryptfs", "cryptocomplete");
2320            return Integer.parseInt(event.getMessage());
2321        } catch (NumberFormatException e) {
2322            // Bad result - unexpected.
2323            Slog.w(TAG, "Unable to parse result from cryptfs cryptocomplete");
2324            return ENCRYPTION_STATE_ERROR_UNKNOWN;
2325        } catch (NativeDaemonConnectorException e) {
2326            // Something bad happened.
2327            Slog.w(TAG, "Error in communicating with cryptfs in validating");
2328            return ENCRYPTION_STATE_ERROR_UNKNOWN;
2329        }
2330    }
2331
2332    @Override
2333    public int decryptStorage(String password) {
2334        if (TextUtils.isEmpty(password)) {
2335            throw new IllegalArgumentException("password cannot be empty");
2336        }
2337
2338        mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2339                "no permission to access the crypt keeper");
2340
2341        waitForReady();
2342
2343        if (DEBUG_EVENTS) {
2344            Slog.i(TAG, "decrypting storage...");
2345        }
2346
2347        final NativeDaemonEvent event;
2348        try {
2349            event = mCryptConnector.execute("cryptfs", "checkpw", new SensitiveArg(password));
2350
2351            final int code = Integer.parseInt(event.getMessage());
2352            if (code == 0) {
2353                // Decrypt was successful. Post a delayed message before restarting in order
2354                // to let the UI to clear itself
2355                mHandler.postDelayed(new Runnable() {
2356                    public void run() {
2357                        try {
2358                            mCryptConnector.execute("cryptfs", "restart");
2359                        } catch (NativeDaemonConnectorException e) {
2360                            Slog.e(TAG, "problem executing in background", e);
2361                        }
2362                    }
2363                }, 1000); // 1 second
2364            }
2365
2366            return code;
2367        } catch (NativeDaemonConnectorException e) {
2368            // Decryption failed
2369            return e.getCode();
2370        }
2371    }
2372
2373    public int encryptStorage(int type, String password) {
2374        if (TextUtils.isEmpty(password) && type != StorageManager.CRYPT_TYPE_DEFAULT) {
2375            throw new IllegalArgumentException("password cannot be empty");
2376        }
2377
2378        mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2379            "no permission to access the crypt keeper");
2380
2381        waitForReady();
2382
2383        if (DEBUG_EVENTS) {
2384            Slog.i(TAG, "encrypting storage...");
2385        }
2386
2387        try {
2388            mCryptConnector.execute("cryptfs", "enablecrypto", "inplace", CRYPTO_TYPES[type],
2389                               new SensitiveArg(password));
2390        } catch (NativeDaemonConnectorException e) {
2391            // Encryption failed
2392            return e.getCode();
2393        }
2394
2395        return 0;
2396    }
2397
2398    /** Set the password for encrypting the master key.
2399     *  @param type One of the CRYPTO_TYPE_XXX consts defined in StorageManager.
2400     *  @param password The password to set.
2401     */
2402    public int changeEncryptionPassword(int type, String password) {
2403        mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2404            "no permission to access the crypt keeper");
2405
2406        waitForReady();
2407
2408        if (DEBUG_EVENTS) {
2409            Slog.i(TAG, "changing encryption password...");
2410        }
2411
2412        try {
2413            NativeDaemonEvent event = mCryptConnector.execute("cryptfs", "changepw", CRYPTO_TYPES[type],
2414                        new SensitiveArg(password));
2415            return Integer.parseInt(event.getMessage());
2416        } catch (NativeDaemonConnectorException e) {
2417            // Encryption failed
2418            return e.getCode();
2419        }
2420    }
2421
2422    /**
2423     * Validate a user-supplied password string with cryptfs
2424     */
2425    @Override
2426    public int verifyEncryptionPassword(String password) throws RemoteException {
2427        // Only the system process is permitted to validate passwords
2428        if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
2429            throw new SecurityException("no permission to access the crypt keeper");
2430        }
2431
2432        mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
2433            "no permission to access the crypt keeper");
2434
2435        if (TextUtils.isEmpty(password)) {
2436            throw new IllegalArgumentException("password cannot be empty");
2437        }
2438
2439        waitForReady();
2440
2441        if (DEBUG_EVENTS) {
2442            Slog.i(TAG, "validating encryption password...");
2443        }
2444
2445        final NativeDaemonEvent event;
2446        try {
2447            event = mCryptConnector.execute("cryptfs", "verifypw", new SensitiveArg(password));
2448            Slog.i(TAG, "cryptfs verifypw => " + event.getMessage());
2449            return Integer.parseInt(event.getMessage());
2450        } catch (NativeDaemonConnectorException e) {
2451            // Encryption failed
2452            return e.getCode();
2453        }
2454    }
2455
2456    /**
2457     * Get the type of encryption used to encrypt the master key.
2458     * @return The type, one of the CRYPT_TYPE_XXX consts from StorageManager.
2459     */
2460    @Override
2461    public int getPasswordType() {
2462
2463        waitForReady();
2464
2465        final NativeDaemonEvent event;
2466        try {
2467            event = mCryptConnector.execute("cryptfs", "getpwtype");
2468            for (int i = 0; i < CRYPTO_TYPES.length; ++i) {
2469                if (CRYPTO_TYPES[i].equals(event.getMessage()))
2470                    return i;
2471            }
2472
2473            throw new IllegalStateException("unexpected return from cryptfs");
2474        } catch (NativeDaemonConnectorException e) {
2475            throw e.rethrowAsParcelableException();
2476        }
2477    }
2478
2479    /**
2480     * Set a field in the crypto header.
2481     * @param field field to set
2482     * @param contents contents to set in field
2483     */
2484    @Override
2485    public void setField(String field, String contents) throws RemoteException {
2486
2487        waitForReady();
2488
2489        final NativeDaemonEvent event;
2490        try {
2491            event = mCryptConnector.execute("cryptfs", "setfield", field, contents);
2492        } catch (NativeDaemonConnectorException e) {
2493            throw e.rethrowAsParcelableException();
2494        }
2495    }
2496
2497    /**
2498     * Gets a field from the crypto header.
2499     * @param field field to get
2500     * @return contents of field
2501     */
2502    @Override
2503    public String getField(String field) throws RemoteException {
2504
2505        waitForReady();
2506
2507        final NativeDaemonEvent event;
2508        try {
2509            final String[] contents = NativeDaemonEvent.filterMessageList(
2510                    mCryptConnector.executeForList("cryptfs", "getfield", field),
2511                    VoldResponseCode.CryptfsGetfieldResult);
2512            String result = new String();
2513            for (String content : contents) {
2514                result += content;
2515            }
2516            return result;
2517        } catch (NativeDaemonConnectorException e) {
2518            throw e.rethrowAsParcelableException();
2519        }
2520    }
2521
2522    @Override
2523    public String getPassword() throws RemoteException {
2524        mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE,
2525                "only keyguard can retrieve password");
2526        if (!isReady()) {
2527            return new String();
2528        }
2529
2530        final NativeDaemonEvent event;
2531        try {
2532            event = mCryptConnector.execute("cryptfs", "getpw");
2533            if ("-1".equals(event.getMessage())) {
2534                // -1 equals no password
2535                return null;
2536            }
2537            return event.getMessage();
2538        } catch (NativeDaemonConnectorException e) {
2539            throw e.rethrowAsParcelableException();
2540        } catch (IllegalArgumentException e) {
2541            Slog.e(TAG, "Invalid response to getPassword");
2542            return null;
2543        }
2544    }
2545
2546    @Override
2547    public void clearPassword() throws RemoteException {
2548        if (!isReady()) {
2549            return;
2550        }
2551
2552        final NativeDaemonEvent event;
2553        try {
2554            event = mCryptConnector.execute("cryptfs", "clearpw");
2555        } catch (NativeDaemonConnectorException e) {
2556            throw e.rethrowAsParcelableException();
2557        }
2558    }
2559
2560    @Override
2561    public int mkdirs(String callingPkg, String appPath) {
2562        final int userId = UserHandle.getUserId(Binder.getCallingUid());
2563        final UserEnvironment userEnv = new UserEnvironment(userId);
2564
2565        // Validate that reported package name belongs to caller
2566        final AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(
2567                Context.APP_OPS_SERVICE);
2568        appOps.checkPackage(Binder.getCallingUid(), callingPkg);
2569
2570        File appFile = null;
2571        try {
2572            appFile = new File(appPath).getCanonicalFile();
2573        } catch (IOException e) {
2574            Slog.e(TAG, "Failed to resolve " + appPath + ": " + e);
2575            return -1;
2576        }
2577
2578        // Try translating the app path into a vold path, but require that it
2579        // belong to the calling package.
2580        if (FileUtils.contains(userEnv.buildExternalStorageAppDataDirs(callingPkg), appFile) ||
2581                FileUtils.contains(userEnv.buildExternalStorageAppObbDirs(callingPkg), appFile) ||
2582                FileUtils.contains(userEnv.buildExternalStorageAppMediaDirs(callingPkg), appFile)) {
2583            appPath = appFile.getAbsolutePath();
2584            if (!appPath.endsWith("/")) {
2585                appPath = appPath + "/";
2586            }
2587
2588            try {
2589                mConnector.execute("volume", "mkdirs", appPath);
2590                return 0;
2591            } catch (NativeDaemonConnectorException e) {
2592                return e.getCode();
2593            }
2594        }
2595
2596        throw new SecurityException("Invalid mkdirs path: " + appFile);
2597    }
2598
2599    @Override
2600    public StorageVolume[] getVolumeList(int uid, String packageName) {
2601        final ArrayList<StorageVolume> res = new ArrayList<>();
2602        boolean foundPrimary = false;
2603
2604        final int userId = UserHandle.getUserId(uid);
2605        final boolean reportUnmounted = !mMountServiceInternal.hasExternalStorage(
2606                uid, packageName);
2607
2608        synchronized (mLock) {
2609            for (int i = 0; i < mVolumes.size(); i++) {
2610                final VolumeInfo vol = mVolumes.valueAt(i);
2611                if (vol.isVisibleToUser(userId)) {
2612                    final StorageVolume userVol = vol.buildStorageVolume(mContext, userId,
2613                            reportUnmounted);
2614                    if (vol.isPrimary()) {
2615                        res.add(0, userVol);
2616                        foundPrimary = true;
2617                    } else {
2618                        res.add(userVol);
2619                    }
2620                }
2621            }
2622        }
2623
2624        if (!foundPrimary) {
2625            Log.w(TAG, "No primary storage defined yet; hacking together a stub");
2626
2627            final boolean primaryPhysical = SystemProperties.getBoolean(
2628                    StorageManager.PROP_PRIMARY_PHYSICAL, false);
2629
2630            final String id = "stub_primary";
2631            final File path = Environment.getLegacyExternalStorageDirectory();
2632            final String description = mContext.getString(android.R.string.unknownName);
2633            final boolean primary = true;
2634            final boolean removable = primaryPhysical;
2635            final boolean emulated = !primaryPhysical;
2636            final long mtpReserveSize = 0L;
2637            final boolean allowMassStorage = false;
2638            final long maxFileSize = 0L;
2639            final UserHandle owner = new UserHandle(userId);
2640            final String uuid = null;
2641            final String state = Environment.MEDIA_REMOVED;
2642
2643            res.add(0, new StorageVolume(id, StorageVolume.STORAGE_ID_INVALID, path,
2644                    description, primary, removable, emulated, mtpReserveSize,
2645                    allowMassStorage, maxFileSize, owner, uuid, state));
2646        }
2647
2648        return res.toArray(new StorageVolume[res.size()]);
2649    }
2650
2651    @Override
2652    public DiskInfo[] getDisks() {
2653        synchronized (mLock) {
2654            final DiskInfo[] res = new DiskInfo[mDisks.size()];
2655            for (int i = 0; i < mDisks.size(); i++) {
2656                res[i] = mDisks.valueAt(i);
2657            }
2658            return res;
2659        }
2660    }
2661
2662    @Override
2663    public VolumeInfo[] getVolumes(int flags) {
2664        synchronized (mLock) {
2665            final VolumeInfo[] res = new VolumeInfo[mVolumes.size()];
2666            for (int i = 0; i < mVolumes.size(); i++) {
2667                res[i] = mVolumes.valueAt(i);
2668            }
2669            return res;
2670        }
2671    }
2672
2673    @Override
2674    public VolumeRecord[] getVolumeRecords(int flags) {
2675        synchronized (mLock) {
2676            final VolumeRecord[] res = new VolumeRecord[mRecords.size()];
2677            for (int i = 0; i < mRecords.size(); i++) {
2678                res[i] = mRecords.valueAt(i);
2679            }
2680            return res;
2681        }
2682    }
2683
2684    private void addObbStateLocked(ObbState obbState) throws RemoteException {
2685        final IBinder binder = obbState.getBinder();
2686        List<ObbState> obbStates = mObbMounts.get(binder);
2687
2688        if (obbStates == null) {
2689            obbStates = new ArrayList<ObbState>();
2690            mObbMounts.put(binder, obbStates);
2691        } else {
2692            for (final ObbState o : obbStates) {
2693                if (o.rawPath.equals(obbState.rawPath)) {
2694                    throw new IllegalStateException("Attempt to add ObbState twice. "
2695                            + "This indicates an error in the MountService logic.");
2696                }
2697            }
2698        }
2699
2700        obbStates.add(obbState);
2701        try {
2702            obbState.link();
2703        } catch (RemoteException e) {
2704            /*
2705             * The binder died before we could link it, so clean up our state
2706             * and return failure.
2707             */
2708            obbStates.remove(obbState);
2709            if (obbStates.isEmpty()) {
2710                mObbMounts.remove(binder);
2711            }
2712
2713            // Rethrow the error so mountObb can get it
2714            throw e;
2715        }
2716
2717        mObbPathToStateMap.put(obbState.rawPath, obbState);
2718    }
2719
2720    private void removeObbStateLocked(ObbState obbState) {
2721        final IBinder binder = obbState.getBinder();
2722        final List<ObbState> obbStates = mObbMounts.get(binder);
2723        if (obbStates != null) {
2724            if (obbStates.remove(obbState)) {
2725                obbState.unlink();
2726            }
2727            if (obbStates.isEmpty()) {
2728                mObbMounts.remove(binder);
2729            }
2730        }
2731
2732        mObbPathToStateMap.remove(obbState.rawPath);
2733    }
2734
2735    private class ObbActionHandler extends Handler {
2736        private boolean mBound = false;
2737        private final List<ObbAction> mActions = new LinkedList<ObbAction>();
2738
2739        ObbActionHandler(Looper l) {
2740            super(l);
2741        }
2742
2743        @Override
2744        public void handleMessage(Message msg) {
2745            switch (msg.what) {
2746                case OBB_RUN_ACTION: {
2747                    final ObbAction action = (ObbAction) msg.obj;
2748
2749                    if (DEBUG_OBB)
2750                        Slog.i(TAG, "OBB_RUN_ACTION: " + action.toString());
2751
2752                    // If a bind was already initiated we don't really
2753                    // need to do anything. The pending install
2754                    // will be processed later on.
2755                    if (!mBound) {
2756                        // If this is the only one pending we might
2757                        // have to bind to the service again.
2758                        if (!connectToService()) {
2759                            Slog.e(TAG, "Failed to bind to media container service");
2760                            action.handleError();
2761                            return;
2762                        }
2763                    }
2764
2765                    mActions.add(action);
2766                    break;
2767                }
2768                case OBB_MCS_BOUND: {
2769                    if (DEBUG_OBB)
2770                        Slog.i(TAG, "OBB_MCS_BOUND");
2771                    if (msg.obj != null) {
2772                        mContainerService = (IMediaContainerService) msg.obj;
2773                    }
2774                    if (mContainerService == null) {
2775                        // Something seriously wrong. Bail out
2776                        Slog.e(TAG, "Cannot bind to media container service");
2777                        for (ObbAction action : mActions) {
2778                            // Indicate service bind error
2779                            action.handleError();
2780                        }
2781                        mActions.clear();
2782                    } else if (mActions.size() > 0) {
2783                        final ObbAction action = mActions.get(0);
2784                        if (action != null) {
2785                            action.execute(this);
2786                        }
2787                    } else {
2788                        // Should never happen ideally.
2789                        Slog.w(TAG, "Empty queue");
2790                    }
2791                    break;
2792                }
2793                case OBB_MCS_RECONNECT: {
2794                    if (DEBUG_OBB)
2795                        Slog.i(TAG, "OBB_MCS_RECONNECT");
2796                    if (mActions.size() > 0) {
2797                        if (mBound) {
2798                            disconnectService();
2799                        }
2800                        if (!connectToService()) {
2801                            Slog.e(TAG, "Failed to bind to media container service");
2802                            for (ObbAction action : mActions) {
2803                                // Indicate service bind error
2804                                action.handleError();
2805                            }
2806                            mActions.clear();
2807                        }
2808                    }
2809                    break;
2810                }
2811                case OBB_MCS_UNBIND: {
2812                    if (DEBUG_OBB)
2813                        Slog.i(TAG, "OBB_MCS_UNBIND");
2814
2815                    // Delete pending install
2816                    if (mActions.size() > 0) {
2817                        mActions.remove(0);
2818                    }
2819                    if (mActions.size() == 0) {
2820                        if (mBound) {
2821                            disconnectService();
2822                        }
2823                    } else {
2824                        // There are more pending requests in queue.
2825                        // Just post MCS_BOUND message to trigger processing
2826                        // of next pending install.
2827                        mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND);
2828                    }
2829                    break;
2830                }
2831                case OBB_FLUSH_MOUNT_STATE: {
2832                    final String path = (String) msg.obj;
2833
2834                    if (DEBUG_OBB)
2835                        Slog.i(TAG, "Flushing all OBB state for path " + path);
2836
2837                    synchronized (mObbMounts) {
2838                        final List<ObbState> obbStatesToRemove = new LinkedList<ObbState>();
2839
2840                        final Iterator<ObbState> i = mObbPathToStateMap.values().iterator();
2841                        while (i.hasNext()) {
2842                            final ObbState state = i.next();
2843
2844                            /*
2845                             * If this entry's source file is in the volume path
2846                             * that got unmounted, remove it because it's no
2847                             * longer valid.
2848                             */
2849                            if (state.canonicalPath.startsWith(path)) {
2850                                obbStatesToRemove.add(state);
2851                            }
2852                        }
2853
2854                        for (final ObbState obbState : obbStatesToRemove) {
2855                            if (DEBUG_OBB)
2856                                Slog.i(TAG, "Removing state for " + obbState.rawPath);
2857
2858                            removeObbStateLocked(obbState);
2859
2860                            try {
2861                                obbState.token.onObbResult(obbState.rawPath, obbState.nonce,
2862                                        OnObbStateChangeListener.UNMOUNTED);
2863                            } catch (RemoteException e) {
2864                                Slog.i(TAG, "Couldn't send unmount notification for  OBB: "
2865                                        + obbState.rawPath);
2866                            }
2867                        }
2868                    }
2869                    break;
2870                }
2871            }
2872        }
2873
2874        private boolean connectToService() {
2875            if (DEBUG_OBB)
2876                Slog.i(TAG, "Trying to bind to DefaultContainerService");
2877
2878            Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
2879            if (mContext.bindServiceAsUser(service, mDefContainerConn, Context.BIND_AUTO_CREATE,
2880                    UserHandle.OWNER)) {
2881                mBound = true;
2882                return true;
2883            }
2884            return false;
2885        }
2886
2887        private void disconnectService() {
2888            mContainerService = null;
2889            mBound = false;
2890            mContext.unbindService(mDefContainerConn);
2891        }
2892    }
2893
2894    abstract class ObbAction {
2895        private static final int MAX_RETRIES = 3;
2896        private int mRetries;
2897
2898        ObbState mObbState;
2899
2900        ObbAction(ObbState obbState) {
2901            mObbState = obbState;
2902        }
2903
2904        public void execute(ObbActionHandler handler) {
2905            try {
2906                if (DEBUG_OBB)
2907                    Slog.i(TAG, "Starting to execute action: " + toString());
2908                mRetries++;
2909                if (mRetries > MAX_RETRIES) {
2910                    Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
2911                    mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
2912                    handleError();
2913                    return;
2914                } else {
2915                    handleExecute();
2916                    if (DEBUG_OBB)
2917                        Slog.i(TAG, "Posting install MCS_UNBIND");
2918                    mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
2919                }
2920            } catch (RemoteException e) {
2921                if (DEBUG_OBB)
2922                    Slog.i(TAG, "Posting install MCS_RECONNECT");
2923                mObbActionHandler.sendEmptyMessage(OBB_MCS_RECONNECT);
2924            } catch (Exception e) {
2925                if (DEBUG_OBB)
2926                    Slog.d(TAG, "Error handling OBB action", e);
2927                handleError();
2928                mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
2929            }
2930        }
2931
2932        abstract void handleExecute() throws RemoteException, IOException;
2933        abstract void handleError();
2934
2935        protected ObbInfo getObbInfo() throws IOException {
2936            ObbInfo obbInfo;
2937            try {
2938                obbInfo = mContainerService.getObbInfo(mObbState.ownerPath);
2939            } catch (RemoteException e) {
2940                Slog.d(TAG, "Couldn't call DefaultContainerService to fetch OBB info for "
2941                        + mObbState.ownerPath);
2942                obbInfo = null;
2943            }
2944            if (obbInfo == null) {
2945                throw new IOException("Couldn't read OBB file: " + mObbState.ownerPath);
2946            }
2947            return obbInfo;
2948        }
2949
2950        protected void sendNewStatusOrIgnore(int status) {
2951            if (mObbState == null || mObbState.token == null) {
2952                return;
2953            }
2954
2955            try {
2956                mObbState.token.onObbResult(mObbState.rawPath, mObbState.nonce, status);
2957            } catch (RemoteException e) {
2958                Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
2959            }
2960        }
2961    }
2962
2963    class MountObbAction extends ObbAction {
2964        private final String mKey;
2965        private final int mCallingUid;
2966
2967        MountObbAction(ObbState obbState, String key, int callingUid) {
2968            super(obbState);
2969            mKey = key;
2970            mCallingUid = callingUid;
2971        }
2972
2973        @Override
2974        public void handleExecute() throws IOException, RemoteException {
2975            waitForReady();
2976            warnOnNotMounted();
2977
2978            final ObbInfo obbInfo = getObbInfo();
2979
2980            if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mCallingUid)) {
2981                Slog.w(TAG, "Denied attempt to mount OBB " + obbInfo.filename
2982                        + " which is owned by " + obbInfo.packageName);
2983                sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
2984                return;
2985            }
2986
2987            final boolean isMounted;
2988            synchronized (mObbMounts) {
2989                isMounted = mObbPathToStateMap.containsKey(mObbState.rawPath);
2990            }
2991            if (isMounted) {
2992                Slog.w(TAG, "Attempt to mount OBB which is already mounted: " + obbInfo.filename);
2993                sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_ALREADY_MOUNTED);
2994                return;
2995            }
2996
2997            final String hashedKey;
2998            if (mKey == null) {
2999                hashedKey = "none";
3000            } else {
3001                try {
3002                    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
3003
3004                    KeySpec ks = new PBEKeySpec(mKey.toCharArray(), obbInfo.salt,
3005                            PBKDF2_HASH_ROUNDS, CRYPTO_ALGORITHM_KEY_SIZE);
3006                    SecretKey key = factory.generateSecret(ks);
3007                    BigInteger bi = new BigInteger(key.getEncoded());
3008                    hashedKey = bi.toString(16);
3009                } catch (NoSuchAlgorithmException e) {
3010                    Slog.e(TAG, "Could not load PBKDF2 algorithm", e);
3011                    sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
3012                    return;
3013                } catch (InvalidKeySpecException e) {
3014                    Slog.e(TAG, "Invalid key spec when loading PBKDF2 algorithm", e);
3015                    sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
3016                    return;
3017                }
3018            }
3019
3020            int rc = StorageResultCode.OperationSucceeded;
3021            try {
3022                mConnector.execute("obb", "mount", mObbState.voldPath, new SensitiveArg(hashedKey),
3023                        mObbState.ownerGid);
3024            } catch (NativeDaemonConnectorException e) {
3025                int code = e.getCode();
3026                if (code != VoldResponseCode.OpFailedStorageBusy) {
3027                    rc = StorageResultCode.OperationFailedInternalError;
3028                }
3029            }
3030
3031            if (rc == StorageResultCode.OperationSucceeded) {
3032                if (DEBUG_OBB)
3033                    Slog.d(TAG, "Successfully mounted OBB " + mObbState.voldPath);
3034
3035                synchronized (mObbMounts) {
3036                    addObbStateLocked(mObbState);
3037                }
3038
3039                sendNewStatusOrIgnore(OnObbStateChangeListener.MOUNTED);
3040            } else {
3041                Slog.e(TAG, "Couldn't mount OBB file: " + rc);
3042
3043                sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT);
3044            }
3045        }
3046
3047        @Override
3048        public void handleError() {
3049            sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
3050        }
3051
3052        @Override
3053        public String toString() {
3054            StringBuilder sb = new StringBuilder();
3055            sb.append("MountObbAction{");
3056            sb.append(mObbState);
3057            sb.append('}');
3058            return sb.toString();
3059        }
3060    }
3061
3062    class UnmountObbAction extends ObbAction {
3063        private final boolean mForceUnmount;
3064
3065        UnmountObbAction(ObbState obbState, boolean force) {
3066            super(obbState);
3067            mForceUnmount = force;
3068        }
3069
3070        @Override
3071        public void handleExecute() throws IOException {
3072            waitForReady();
3073            warnOnNotMounted();
3074
3075            final ObbInfo obbInfo = getObbInfo();
3076
3077            final ObbState existingState;
3078            synchronized (mObbMounts) {
3079                existingState = mObbPathToStateMap.get(mObbState.rawPath);
3080            }
3081
3082            if (existingState == null) {
3083                sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_NOT_MOUNTED);
3084                return;
3085            }
3086
3087            if (existingState.ownerGid != mObbState.ownerGid) {
3088                Slog.w(TAG, "Permission denied attempting to unmount OBB " + existingState.rawPath
3089                        + " (owned by GID " + existingState.ownerGid + ")");
3090                sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
3091                return;
3092            }
3093
3094            int rc = StorageResultCode.OperationSucceeded;
3095            try {
3096                final Command cmd = new Command("obb", "unmount", mObbState.voldPath);
3097                if (mForceUnmount) {
3098                    cmd.appendArg("force");
3099                }
3100                mConnector.execute(cmd);
3101            } catch (NativeDaemonConnectorException e) {
3102                int code = e.getCode();
3103                if (code == VoldResponseCode.OpFailedStorageBusy) {
3104                    rc = StorageResultCode.OperationFailedStorageBusy;
3105                } else if (code == VoldResponseCode.OpFailedStorageNotFound) {
3106                    // If it's not mounted then we've already won.
3107                    rc = StorageResultCode.OperationSucceeded;
3108                } else {
3109                    rc = StorageResultCode.OperationFailedInternalError;
3110                }
3111            }
3112
3113            if (rc == StorageResultCode.OperationSucceeded) {
3114                synchronized (mObbMounts) {
3115                    removeObbStateLocked(existingState);
3116                }
3117
3118                sendNewStatusOrIgnore(OnObbStateChangeListener.UNMOUNTED);
3119            } else {
3120                Slog.w(TAG, "Could not unmount OBB: " + existingState);
3121                sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_UNMOUNT);
3122            }
3123        }
3124
3125        @Override
3126        public void handleError() {
3127            sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
3128        }
3129
3130        @Override
3131        public String toString() {
3132            StringBuilder sb = new StringBuilder();
3133            sb.append("UnmountObbAction{");
3134            sb.append(mObbState);
3135            sb.append(",force=");
3136            sb.append(mForceUnmount);
3137            sb.append('}');
3138            return sb.toString();
3139        }
3140    }
3141
3142    @VisibleForTesting
3143    public static String buildObbPath(final String canonicalPath, int userId, boolean forVold) {
3144        // TODO: allow caller to provide Environment for full testing
3145        // TODO: extend to support OBB mounts on secondary external storage
3146
3147        // Only adjust paths when storage is emulated
3148        if (!Environment.isExternalStorageEmulated()) {
3149            return canonicalPath;
3150        }
3151
3152        String path = canonicalPath.toString();
3153
3154        // First trim off any external storage prefix
3155        final UserEnvironment userEnv = new UserEnvironment(userId);
3156
3157        // /storage/emulated/0
3158        final String externalPath = userEnv.getExternalStorageDirectory().getAbsolutePath();
3159        // /storage/emulated_legacy
3160        final String legacyExternalPath = Environment.getLegacyExternalStorageDirectory()
3161                .getAbsolutePath();
3162
3163        if (path.startsWith(externalPath)) {
3164            path = path.substring(externalPath.length() + 1);
3165        } else if (path.startsWith(legacyExternalPath)) {
3166            path = path.substring(legacyExternalPath.length() + 1);
3167        } else {
3168            return canonicalPath;
3169        }
3170
3171        // Handle special OBB paths on emulated storage
3172        final String obbPath = "Android/obb";
3173        if (path.startsWith(obbPath)) {
3174            path = path.substring(obbPath.length() + 1);
3175
3176            final UserEnvironment ownerEnv = new UserEnvironment(UserHandle.USER_OWNER);
3177            return new File(ownerEnv.buildExternalStorageAndroidObbDirs()[0], path)
3178                    .getAbsolutePath();
3179        }
3180
3181        // Handle normal external storage paths
3182        return new File(userEnv.getExternalStorageDirectory(), path).getAbsolutePath();
3183    }
3184
3185    private static class Callbacks extends Handler {
3186        private static final int MSG_STORAGE_STATE_CHANGED = 1;
3187        private static final int MSG_VOLUME_STATE_CHANGED = 2;
3188        private static final int MSG_VOLUME_RECORD_CHANGED = 3;
3189        private static final int MSG_VOLUME_FORGOTTEN = 4;
3190        private static final int MSG_DISK_SCANNED = 5;
3191        private static final int MSG_DISK_DESTROYED = 6;
3192
3193        private final RemoteCallbackList<IMountServiceListener>
3194                mCallbacks = new RemoteCallbackList<>();
3195
3196        public Callbacks(Looper looper) {
3197            super(looper);
3198        }
3199
3200        public void register(IMountServiceListener callback) {
3201            mCallbacks.register(callback);
3202        }
3203
3204        public void unregister(IMountServiceListener callback) {
3205            mCallbacks.unregister(callback);
3206        }
3207
3208        @Override
3209        public void handleMessage(Message msg) {
3210            final SomeArgs args = (SomeArgs) msg.obj;
3211            final int n = mCallbacks.beginBroadcast();
3212            for (int i = 0; i < n; i++) {
3213                final IMountServiceListener callback = mCallbacks.getBroadcastItem(i);
3214                try {
3215                    invokeCallback(callback, msg.what, args);
3216                } catch (RemoteException ignored) {
3217                }
3218            }
3219            mCallbacks.finishBroadcast();
3220            args.recycle();
3221        }
3222
3223        private void invokeCallback(IMountServiceListener callback, int what, SomeArgs args)
3224                throws RemoteException {
3225            switch (what) {
3226                case MSG_STORAGE_STATE_CHANGED: {
3227                    callback.onStorageStateChanged((String) args.arg1, (String) args.arg2,
3228                            (String) args.arg3);
3229                    break;
3230                }
3231                case MSG_VOLUME_STATE_CHANGED: {
3232                    callback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3);
3233                    break;
3234                }
3235                case MSG_VOLUME_RECORD_CHANGED: {
3236                    callback.onVolumeRecordChanged((VolumeRecord) args.arg1);
3237                    break;
3238                }
3239                case MSG_VOLUME_FORGOTTEN: {
3240                    callback.onVolumeForgotten((String) args.arg1);
3241                    break;
3242                }
3243                case MSG_DISK_SCANNED: {
3244                    callback.onDiskScanned((DiskInfo) args.arg1, args.argi2);
3245                    break;
3246                }
3247                case MSG_DISK_DESTROYED: {
3248                    callback.onDiskDestroyed((DiskInfo) args.arg1);
3249                    break;
3250                }
3251            }
3252        }
3253
3254        private void notifyStorageStateChanged(String path, String oldState, String newState) {
3255            final SomeArgs args = SomeArgs.obtain();
3256            args.arg1 = path;
3257            args.arg2 = oldState;
3258            args.arg3 = newState;
3259            obtainMessage(MSG_STORAGE_STATE_CHANGED, args).sendToTarget();
3260        }
3261
3262        private void notifyVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
3263            final SomeArgs args = SomeArgs.obtain();
3264            args.arg1 = vol.clone();
3265            args.argi2 = oldState;
3266            args.argi3 = newState;
3267            obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget();
3268        }
3269
3270        private void notifyVolumeRecordChanged(VolumeRecord rec) {
3271            final SomeArgs args = SomeArgs.obtain();
3272            args.arg1 = rec.clone();
3273            obtainMessage(MSG_VOLUME_RECORD_CHANGED, args).sendToTarget();
3274        }
3275
3276        private void notifyVolumeForgotten(String fsUuid) {
3277            final SomeArgs args = SomeArgs.obtain();
3278            args.arg1 = fsUuid;
3279            obtainMessage(MSG_VOLUME_FORGOTTEN, args).sendToTarget();
3280        }
3281
3282        private void notifyDiskScanned(DiskInfo disk, int volumeCount) {
3283            final SomeArgs args = SomeArgs.obtain();
3284            args.arg1 = disk.clone();
3285            args.argi2 = volumeCount;
3286            obtainMessage(MSG_DISK_SCANNED, args).sendToTarget();
3287        }
3288
3289        private void notifyDiskDestroyed(DiskInfo disk) {
3290            final SomeArgs args = SomeArgs.obtain();
3291            args.arg1 = disk.clone();
3292            obtainMessage(MSG_DISK_DESTROYED, args).sendToTarget();
3293        }
3294    }
3295
3296    @Override
3297    protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
3298        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
3299
3300        final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ", 160);
3301        synchronized (mLock) {
3302            pw.println("Disks:");
3303            pw.increaseIndent();
3304            for (int i = 0; i < mDisks.size(); i++) {
3305                final DiskInfo disk = mDisks.valueAt(i);
3306                disk.dump(pw);
3307            }
3308            pw.decreaseIndent();
3309
3310            pw.println();
3311            pw.println("Volumes:");
3312            pw.increaseIndent();
3313            for (int i = 0; i < mVolumes.size(); i++) {
3314                final VolumeInfo vol = mVolumes.valueAt(i);
3315                if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.id)) continue;
3316                vol.dump(pw);
3317            }
3318            pw.decreaseIndent();
3319
3320            pw.println();
3321            pw.println("Records:");
3322            pw.increaseIndent();
3323            for (int i = 0; i < mRecords.size(); i++) {
3324                final VolumeRecord note = mRecords.valueAt(i);
3325                note.dump(pw);
3326            }
3327            pw.decreaseIndent();
3328
3329            pw.println();
3330            pw.println("Primary storage UUID: " + mPrimaryStorageUuid);
3331            pw.println("Force adoptable: " + mForceAdoptable);
3332        }
3333
3334        synchronized (mObbMounts) {
3335            pw.println();
3336            pw.println("mObbMounts:");
3337            pw.increaseIndent();
3338            final Iterator<Entry<IBinder, List<ObbState>>> binders = mObbMounts.entrySet()
3339                    .iterator();
3340            while (binders.hasNext()) {
3341                Entry<IBinder, List<ObbState>> e = binders.next();
3342                pw.println(e.getKey() + ":");
3343                pw.increaseIndent();
3344                final List<ObbState> obbStates = e.getValue();
3345                for (final ObbState obbState : obbStates) {
3346                    pw.println(obbState);
3347                }
3348                pw.decreaseIndent();
3349            }
3350            pw.decreaseIndent();
3351
3352            pw.println();
3353            pw.println("mObbPathToStateMap:");
3354            pw.increaseIndent();
3355            final Iterator<Entry<String, ObbState>> maps = mObbPathToStateMap.entrySet().iterator();
3356            while (maps.hasNext()) {
3357                final Entry<String, ObbState> e = maps.next();
3358                pw.print(e.getKey());
3359                pw.print(" -> ");
3360                pw.println(e.getValue());
3361            }
3362            pw.decreaseIndent();
3363        }
3364
3365        pw.println();
3366        pw.println("mConnection:");
3367        pw.increaseIndent();
3368        mConnector.dump(fd, pw, args);
3369        pw.decreaseIndent();
3370
3371        pw.println();
3372        pw.print("Last maintenance: ");
3373        pw.println(TimeUtils.formatForLogging(mLastMaintenance));
3374    }
3375
3376    /** {@inheritDoc} */
3377    @Override
3378    public void monitor() {
3379        if (mConnector != null) {
3380            mConnector.monitor();
3381        }
3382        if (mCryptConnector != null) {
3383            mCryptConnector.monitor();
3384        }
3385    }
3386
3387    private final class MountServiceInternalImpl extends MountServiceInternal {
3388        // Not guarded by a lock.
3389        private final CopyOnWriteArrayList<ExternalStorageMountPolicy> mPolicies =
3390                new CopyOnWriteArrayList<>();
3391
3392        @Override
3393        public void addExternalStoragePolicy(ExternalStorageMountPolicy policy) {
3394            // No locking - CopyOnWriteArrayList
3395            mPolicies.add(policy);
3396        }
3397
3398        @Override
3399        public void onExternalStoragePolicyChanged(int uid, String packageName) {
3400            final int mountMode = getExternalStorageMountMode(uid, packageName);
3401            remountUidExternalStorage(uid, mountMode);
3402        }
3403
3404        @Override
3405        public int getExternalStorageMountMode(int uid, String packageName) {
3406            // No locking - CopyOnWriteArrayList
3407            int mountMode = Integer.MAX_VALUE;
3408            for (ExternalStorageMountPolicy policy : mPolicies) {
3409                final int policyMode = policy.getMountMode(uid, packageName);
3410                if (policyMode == Zygote.MOUNT_EXTERNAL_NONE) {
3411                    return Zygote.MOUNT_EXTERNAL_NONE;
3412                }
3413                mountMode = Math.min(mountMode, policyMode);
3414            }
3415            if (mountMode == Integer.MAX_VALUE) {
3416                return Zygote.MOUNT_EXTERNAL_NONE;
3417            }
3418            return mountMode;
3419        }
3420
3421        public boolean hasExternalStorage(int uid, String packageName) {
3422            // No locking - CopyOnWriteArrayList
3423            for (ExternalStorageMountPolicy policy : mPolicies) {
3424                final boolean policyHasStorage = policy.hasExternalStorage(uid, packageName);
3425                if (!policyHasStorage) {
3426                    return false;
3427                }
3428            }
3429            return true;
3430        }
3431    }
3432}
3433