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