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