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