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