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