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