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