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 android.content.pm.PackageManager.PERMISSION_GRANTED;
     20 
     21 import android.Manifest;
     22 import android.app.ActivityManagerNative;
     23 import android.app.AppOpsManager;
     24 import android.content.BroadcastReceiver;
     25 import android.content.ComponentName;
     26 import android.content.Context;
     27 import android.content.Intent;
     28 import android.content.IntentFilter;
     29 import android.content.ServiceConnection;
     30 import android.content.pm.PackageManager;
     31 import android.content.pm.UserInfo;
     32 import android.content.res.Configuration;
     33 import android.content.res.ObbInfo;
     34 import android.content.res.Resources;
     35 import android.content.res.TypedArray;
     36 import android.content.res.XmlResourceParser;
     37 import android.hardware.usb.UsbManager;
     38 import android.net.Uri;
     39 import android.os.Binder;
     40 import android.os.Environment;
     41 import android.os.Environment.UserEnvironment;
     42 import android.os.Handler;
     43 import android.os.HandlerThread;
     44 import android.os.IBinder;
     45 import android.os.Looper;
     46 import android.os.Message;
     47 import android.os.RemoteException;
     48 import android.os.ServiceManager;
     49 import android.os.SystemClock;
     50 import android.os.SystemProperties;
     51 import android.os.UserHandle;
     52 import android.os.UserManager;
     53 import android.os.storage.IMountService;
     54 import android.os.storage.IMountServiceListener;
     55 import android.os.storage.IMountShutdownObserver;
     56 import android.os.storage.IObbActionListener;
     57 import android.os.storage.OnObbStateChangeListener;
     58 import android.os.storage.StorageManager;
     59 import android.os.storage.StorageResultCode;
     60 import android.os.storage.StorageVolume;
     61 import android.text.TextUtils;
     62 import android.util.AttributeSet;
     63 import android.util.Slog;
     64 import android.util.Xml;
     65 
     66 import com.android.internal.annotations.GuardedBy;
     67 import com.android.internal.annotations.VisibleForTesting;
     68 import com.android.internal.app.IMediaContainerService;
     69 import com.android.internal.util.IndentingPrintWriter;
     70 import com.android.internal.util.Preconditions;
     71 import com.android.internal.util.XmlUtils;
     72 import com.android.server.NativeDaemonConnector.Command;
     73 import com.android.server.NativeDaemonConnector.SensitiveArg;
     74 import com.android.server.am.ActivityManagerService;
     75 import com.android.server.pm.PackageManagerService;
     76 import com.android.server.pm.UserManagerService;
     77 import com.google.android.collect.Lists;
     78 import com.google.android.collect.Maps;
     79 
     80 import org.apache.commons.codec.binary.Hex;
     81 import org.apache.commons.codec.DecoderException;
     82 import org.xmlpull.v1.XmlPullParserException;
     83 
     84 import java.io.File;
     85 import java.io.FileDescriptor;
     86 import java.io.FileOutputStream;
     87 import java.io.IOException;
     88 import java.io.PrintWriter;
     89 import java.math.BigInteger;
     90 import java.nio.charset.StandardCharsets;
     91 import java.security.NoSuchAlgorithmException;
     92 import java.security.spec.InvalidKeySpecException;
     93 import java.security.spec.KeySpec;
     94 import java.text.SimpleDateFormat;
     95 import java.util.ArrayList;
     96 import java.util.Date;
     97 import java.util.HashMap;
     98 import java.util.HashSet;
     99 import java.util.Iterator;
    100 import java.util.LinkedList;
    101 import java.util.List;
    102 import java.util.Locale;
    103 import java.util.Map;
    104 import java.util.Map.Entry;
    105 import java.util.concurrent.atomic.AtomicInteger;
    106 import java.util.concurrent.CountDownLatch;
    107 import java.util.concurrent.TimeUnit;
    108 
    109 import javax.crypto.SecretKey;
    110 import javax.crypto.SecretKeyFactory;
    111 import javax.crypto.spec.PBEKeySpec;
    112 
    113 /**
    114  * MountService implements back-end services for platform storage
    115  * management.
    116  * @hide - Applications should use android.os.storage.StorageManager
    117  * to access the MountService.
    118  */
    119 class MountService extends IMountService.Stub
    120         implements INativeDaemonConnectorCallbacks, Watchdog.Monitor {
    121 
    122     // Static direct instance pointer for the tightly-coupled idle service to use
    123     static MountService sSelf = null;
    124 
    125     // TODO: listen for user creation/deletion
    126 
    127     private static final boolean LOCAL_LOGD = false;
    128     private static final boolean DEBUG_UNMOUNT = false;
    129     private static final boolean DEBUG_EVENTS = false;
    130     private static final boolean DEBUG_OBB = false;
    131 
    132     // Disable this since it messes up long-running cryptfs operations.
    133     private static final boolean WATCHDOG_ENABLE = false;
    134 
    135     private static final String TAG = "MountService";
    136 
    137     private static final String VOLD_TAG = "VoldConnector";
    138 
    139     /** Maximum number of ASEC containers allowed to be mounted. */
    140     private static final int MAX_CONTAINERS = 250;
    141 
    142     /*
    143      * Internal vold volume state constants
    144      */
    145     class VolumeState {
    146         public static final int Init       = -1;
    147         public static final int NoMedia    = 0;
    148         public static final int Idle       = 1;
    149         public static final int Pending    = 2;
    150         public static final int Checking   = 3;
    151         public static final int Mounted    = 4;
    152         public static final int Unmounting = 5;
    153         public static final int Formatting = 6;
    154         public static final int Shared     = 7;
    155         public static final int SharedMnt  = 8;
    156     }
    157 
    158     /*
    159      * Internal vold response code constants
    160      */
    161     class VoldResponseCode {
    162         /*
    163          * 100 series - Requestion action was initiated; expect another reply
    164          *              before proceeding with a new command.
    165          */
    166         public static final int VolumeListResult               = 110;
    167         public static final int AsecListResult                 = 111;
    168         public static final int StorageUsersListResult         = 112;
    169         public static final int CryptfsGetfieldResult          = 113;
    170 
    171         /*
    172          * 200 series - Requestion action has been successfully completed.
    173          */
    174         public static final int ShareStatusResult              = 210;
    175         public static final int AsecPathResult                 = 211;
    176         public static final int ShareEnabledResult             = 212;
    177 
    178         /*
    179          * 400 series - Command was accepted, but the requested action
    180          *              did not take place.
    181          */
    182         public static final int OpFailedNoMedia                = 401;
    183         public static final int OpFailedMediaBlank             = 402;
    184         public static final int OpFailedMediaCorrupt           = 403;
    185         public static final int OpFailedVolNotMounted          = 404;
    186         public static final int OpFailedStorageBusy            = 405;
    187         public static final int OpFailedStorageNotFound        = 406;
    188 
    189         /*
    190          * 600 series - Unsolicited broadcasts.
    191          */
    192         public static final int VolumeStateChange              = 605;
    193         public static final int VolumeUuidChange               = 613;
    194         public static final int VolumeUserLabelChange          = 614;
    195         public static final int VolumeDiskInserted             = 630;
    196         public static final int VolumeDiskRemoved              = 631;
    197         public static final int VolumeBadRemoval               = 632;
    198 
    199         /*
    200          * 700 series - fstrim
    201          */
    202         public static final int FstrimCompleted                = 700;
    203     }
    204 
    205     /** List of crypto types.
    206       * These must match CRYPT_TYPE_XXX in cryptfs.h AND their
    207       * corresponding commands in CommandListener.cpp */
    208     public static final String[] CRYPTO_TYPES
    209         = { "password", "default", "pattern", "pin" };
    210 
    211     private final Context mContext;
    212     private final NativeDaemonConnector mConnector;
    213 
    214     private final Object mVolumesLock = new Object();
    215 
    216     /** When defined, base template for user-specific {@link StorageVolume}. */
    217     private StorageVolume mEmulatedTemplate;
    218 
    219     // TODO: separate storage volumes on per-user basis
    220 
    221     @GuardedBy("mVolumesLock")
    222     private final ArrayList<StorageVolume> mVolumes = Lists.newArrayList();
    223     /** Map from path to {@link StorageVolume} */
    224     @GuardedBy("mVolumesLock")
    225     private final HashMap<String, StorageVolume> mVolumesByPath = Maps.newHashMap();
    226     /** Map from path to state */
    227     @GuardedBy("mVolumesLock")
    228     private final HashMap<String, String> mVolumeStates = Maps.newHashMap();
    229 
    230     private volatile boolean mSystemReady = false;
    231 
    232     private PackageManagerService                 mPms;
    233     private boolean                               mUmsEnabling;
    234     private boolean                               mUmsAvailable = false;
    235     // Used as a lock for methods that register/unregister listeners.
    236     final private ArrayList<MountServiceBinderListener> mListeners =
    237             new ArrayList<MountServiceBinderListener>();
    238     private final CountDownLatch mConnectedSignal = new CountDownLatch(1);
    239     private final CountDownLatch mAsecsScanned = new CountDownLatch(1);
    240     private boolean                               mSendUmsConnectedOnBoot = false;
    241 
    242     /**
    243      * Private hash of currently mounted secure containers.
    244      * Used as a lock in methods to manipulate secure containers.
    245      */
    246     final private HashSet<String> mAsecMountSet = new HashSet<String>();
    247 
    248     /**
    249      * The size of the crypto algorithm key in bits for OBB files. Currently
    250      * Twofish is used which takes 128-bit keys.
    251      */
    252     private static final int CRYPTO_ALGORITHM_KEY_SIZE = 128;
    253 
    254     /**
    255      * The number of times to run SHA1 in the PBKDF2 function for OBB files.
    256      * 1024 is reasonably secure and not too slow.
    257      */
    258     private static final int PBKDF2_HASH_ROUNDS = 1024;
    259 
    260     /**
    261      * Mounted OBB tracking information. Used to track the current state of all
    262      * OBBs.
    263      */
    264     final private Map<IBinder, List<ObbState>> mObbMounts = new HashMap<IBinder, List<ObbState>>();
    265 
    266     /** Map from raw paths to {@link ObbState}. */
    267     final private Map<String, ObbState> mObbPathToStateMap = new HashMap<String, ObbState>();
    268 
    269     class ObbState implements IBinder.DeathRecipient {
    270         public ObbState(String rawPath, String canonicalPath, int callingUid,
    271                 IObbActionListener token, int nonce) {
    272             this.rawPath = rawPath;
    273             this.canonicalPath = canonicalPath.toString();
    274 
    275             final int userId = UserHandle.getUserId(callingUid);
    276             this.ownerPath = buildObbPath(canonicalPath, userId, false);
    277             this.voldPath = buildObbPath(canonicalPath, userId, true);
    278 
    279             this.ownerGid = UserHandle.getSharedAppGid(callingUid);
    280             this.token = token;
    281             this.nonce = nonce;
    282         }
    283 
    284         final String rawPath;
    285         final String canonicalPath;
    286         final String ownerPath;
    287         final String voldPath;
    288 
    289         final int ownerGid;
    290 
    291         // Token of remote Binder caller
    292         final IObbActionListener token;
    293 
    294         // Identifier to pass back to the token
    295         final int nonce;
    296 
    297         public IBinder getBinder() {
    298             return token.asBinder();
    299         }
    300 
    301         @Override
    302         public void binderDied() {
    303             ObbAction action = new UnmountObbAction(this, true);
    304             mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
    305         }
    306 
    307         public void link() throws RemoteException {
    308             getBinder().linkToDeath(this, 0);
    309         }
    310 
    311         public void unlink() {
    312             getBinder().unlinkToDeath(this, 0);
    313         }
    314 
    315         @Override
    316         public String toString() {
    317             StringBuilder sb = new StringBuilder("ObbState{");
    318             sb.append("rawPath=").append(rawPath);
    319             sb.append(",canonicalPath=").append(canonicalPath);
    320             sb.append(",ownerPath=").append(ownerPath);
    321             sb.append(",voldPath=").append(voldPath);
    322             sb.append(",ownerGid=").append(ownerGid);
    323             sb.append(",token=").append(token);
    324             sb.append(",binder=").append(getBinder());
    325             sb.append('}');
    326             return sb.toString();
    327         }
    328     }
    329 
    330     // OBB Action Handler
    331     final private ObbActionHandler mObbActionHandler;
    332 
    333     // OBB action handler messages
    334     private static final int OBB_RUN_ACTION = 1;
    335     private static final int OBB_MCS_BOUND = 2;
    336     private static final int OBB_MCS_UNBIND = 3;
    337     private static final int OBB_MCS_RECONNECT = 4;
    338     private static final int OBB_FLUSH_MOUNT_STATE = 5;
    339 
    340     /*
    341      * Default Container Service information
    342      */
    343     static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
    344             "com.android.defcontainer", "com.android.defcontainer.DefaultContainerService");
    345 
    346     final private DefaultContainerConnection mDefContainerConn = new DefaultContainerConnection();
    347 
    348     class DefaultContainerConnection implements ServiceConnection {
    349         public void onServiceConnected(ComponentName name, IBinder service) {
    350             if (DEBUG_OBB)
    351                 Slog.i(TAG, "onServiceConnected");
    352             IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(service);
    353             mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_MCS_BOUND, imcs));
    354         }
    355 
    356         public void onServiceDisconnected(ComponentName name) {
    357             if (DEBUG_OBB)
    358                 Slog.i(TAG, "onServiceDisconnected");
    359         }
    360     };
    361 
    362     // Used in the ObbActionHandler
    363     private IMediaContainerService mContainerService = null;
    364 
    365     // Last fstrim operation tracking
    366     private static final String LAST_FSTRIM_FILE = "last-fstrim";
    367     private final File mLastMaintenanceFile;
    368     private long mLastMaintenance;
    369 
    370     // Handler messages
    371     private static final int H_UNMOUNT_PM_UPDATE = 1;
    372     private static final int H_UNMOUNT_PM_DONE = 2;
    373     private static final int H_UNMOUNT_MS = 3;
    374     private static final int H_SYSTEM_READY = 4;
    375     private static final int H_FSTRIM = 5;
    376 
    377     private static final int RETRY_UNMOUNT_DELAY = 30; // in ms
    378     private static final int MAX_UNMOUNT_RETRIES = 4;
    379 
    380     class UnmountCallBack {
    381         final String path;
    382         final boolean force;
    383         final boolean removeEncryption;
    384         int retries;
    385 
    386         UnmountCallBack(String path, boolean force, boolean removeEncryption) {
    387             retries = 0;
    388             this.path = path;
    389             this.force = force;
    390             this.removeEncryption = removeEncryption;
    391         }
    392 
    393         void handleFinished() {
    394             if (DEBUG_UNMOUNT) Slog.i(TAG, "Unmounting " + path);
    395             doUnmountVolume(path, true, removeEncryption);
    396         }
    397     }
    398 
    399     class UmsEnableCallBack extends UnmountCallBack {
    400         final String method;
    401 
    402         UmsEnableCallBack(String path, String method, boolean force) {
    403             super(path, force, false);
    404             this.method = method;
    405         }
    406 
    407         @Override
    408         void handleFinished() {
    409             super.handleFinished();
    410             doShareUnshareVolume(path, method, true);
    411         }
    412     }
    413 
    414     class ShutdownCallBack extends UnmountCallBack {
    415         MountShutdownLatch mMountShutdownLatch;
    416         ShutdownCallBack(String path, final MountShutdownLatch mountShutdownLatch) {
    417             super(path, true, false);
    418             mMountShutdownLatch = mountShutdownLatch;
    419         }
    420 
    421         @Override
    422         void handleFinished() {
    423             int ret = doUnmountVolume(path, true, removeEncryption);
    424             Slog.i(TAG, "Unmount completed: " + path + ", result code: " + ret);
    425             mMountShutdownLatch.countDown();
    426         }
    427     }
    428 
    429     static class MountShutdownLatch {
    430         private IMountShutdownObserver mObserver;
    431         private AtomicInteger mCount;
    432 
    433         MountShutdownLatch(final IMountShutdownObserver observer, int count) {
    434             mObserver = observer;
    435             mCount = new AtomicInteger(count);
    436         }
    437 
    438         void countDown() {
    439             boolean sendShutdown = false;
    440             if (mCount.decrementAndGet() == 0) {
    441                 sendShutdown = true;
    442             }
    443             if (sendShutdown && mObserver != null) {
    444                 try {
    445                     mObserver.onShutDownComplete(StorageResultCode.OperationSucceeded);
    446                 } catch (RemoteException e) {
    447                     Slog.w(TAG, "RemoteException when shutting down");
    448                 }
    449             }
    450         }
    451     }
    452 
    453     class MountServiceHandler extends Handler {
    454         ArrayList<UnmountCallBack> mForceUnmounts = new ArrayList<UnmountCallBack>();
    455         boolean mUpdatingStatus = false;
    456 
    457         MountServiceHandler(Looper l) {
    458             super(l);
    459         }
    460 
    461         @Override
    462         public void handleMessage(Message msg) {
    463             switch (msg.what) {
    464                 case H_UNMOUNT_PM_UPDATE: {
    465                     if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_UPDATE");
    466                     UnmountCallBack ucb = (UnmountCallBack) msg.obj;
    467                     mForceUnmounts.add(ucb);
    468                     if (DEBUG_UNMOUNT) Slog.i(TAG, " registered = " + mUpdatingStatus);
    469                     // Register only if needed.
    470                     if (!mUpdatingStatus) {
    471                         if (DEBUG_UNMOUNT) Slog.i(TAG, "Updating external media status on PackageManager");
    472                         mUpdatingStatus = true;
    473                         mPms.updateExternalMediaStatus(false, true);
    474                     }
    475                     break;
    476                 }
    477                 case H_UNMOUNT_PM_DONE: {
    478                     if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_DONE");
    479                     if (DEBUG_UNMOUNT) Slog.i(TAG, "Updated status. Processing requests");
    480                     mUpdatingStatus = false;
    481                     int size = mForceUnmounts.size();
    482                     int sizeArr[] = new int[size];
    483                     int sizeArrN = 0;
    484                     // Kill processes holding references first
    485                     ActivityManagerService ams = (ActivityManagerService)
    486                     ServiceManager.getService("activity");
    487                     for (int i = 0; i < size; i++) {
    488                         UnmountCallBack ucb = mForceUnmounts.get(i);
    489                         String path = ucb.path;
    490                         boolean done = false;
    491                         if (!ucb.force) {
    492                             done = true;
    493                         } else {
    494                             int pids[] = getStorageUsers(path);
    495                             if (pids == null || pids.length == 0) {
    496                                 done = true;
    497                             } else {
    498                                 // Eliminate system process here?
    499                                 ams.killPids(pids, "unmount media", true);
    500                                 // Confirm if file references have been freed.
    501                                 pids = getStorageUsers(path);
    502                                 if (pids == null || pids.length == 0) {
    503                                     done = true;
    504                                 }
    505                             }
    506                         }
    507                         if (!done && (ucb.retries < MAX_UNMOUNT_RETRIES)) {
    508                             // Retry again
    509                             Slog.i(TAG, "Retrying to kill storage users again");
    510                             mHandler.sendMessageDelayed(
    511                                     mHandler.obtainMessage(H_UNMOUNT_PM_DONE,
    512                                             ucb.retries++),
    513                                     RETRY_UNMOUNT_DELAY);
    514                         } else {
    515                             if (ucb.retries >= MAX_UNMOUNT_RETRIES) {
    516                                 Slog.i(TAG, "Failed to unmount media inspite of " +
    517                                         MAX_UNMOUNT_RETRIES + " retries. Forcibly killing processes now");
    518                             }
    519                             sizeArr[sizeArrN++] = i;
    520                             mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_MS,
    521                                     ucb));
    522                         }
    523                     }
    524                     // Remove already processed elements from list.
    525                     for (int i = (sizeArrN-1); i >= 0; i--) {
    526                         mForceUnmounts.remove(sizeArr[i]);
    527                     }
    528                     break;
    529                 }
    530                 case H_UNMOUNT_MS: {
    531                     if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_MS");
    532                     UnmountCallBack ucb = (UnmountCallBack) msg.obj;
    533                     ucb.handleFinished();
    534                     break;
    535                 }
    536                 case H_SYSTEM_READY: {
    537                     try {
    538                         handleSystemReady();
    539                     } catch (Exception ex) {
    540                         Slog.e(TAG, "Boot-time mount exception", ex);
    541                     }
    542                     break;
    543                 }
    544                 case H_FSTRIM: {
    545                     waitForReady();
    546                     Slog.i(TAG, "Running fstrim idle maintenance");
    547 
    548                     // Remember when we kicked it off
    549                     try {
    550                         mLastMaintenance = System.currentTimeMillis();
    551                         mLastMaintenanceFile.setLastModified(mLastMaintenance);
    552                     } catch (Exception e) {
    553                         Slog.e(TAG, "Unable to record last fstrim!");
    554                     }
    555 
    556                     try {
    557                         // This method must be run on the main (handler) thread,
    558                         // so it is safe to directly call into vold.
    559                         mConnector.execute("fstrim", "dotrim");
    560                         EventLogTags.writeFstrimStart(SystemClock.elapsedRealtime());
    561                     } catch (NativeDaemonConnectorException ndce) {
    562                         Slog.e(TAG, "Failed to run fstrim!");
    563                     }
    564 
    565                     // invoke the completion callback, if any
    566                     Runnable callback = (Runnable) msg.obj;
    567                     if (callback != null) {
    568                         callback.run();
    569                     }
    570                     break;
    571                 }
    572             }
    573         }
    574     };
    575 
    576     private final Handler mHandler;
    577 
    578     void waitForAsecScan() {
    579         waitForLatch(mAsecsScanned);
    580     }
    581 
    582     private void waitForReady() {
    583         waitForLatch(mConnectedSignal);
    584     }
    585 
    586     private void waitForLatch(CountDownLatch latch) {
    587         for (;;) {
    588             try {
    589                 if (latch.await(5000, TimeUnit.MILLISECONDS)) {
    590                     return;
    591                 } else {
    592                     Slog.w(TAG, "Thread " + Thread.currentThread().getName()
    593                             + " still waiting for MountService ready...");
    594                 }
    595             } catch (InterruptedException e) {
    596                 Slog.w(TAG, "Interrupt while waiting for MountService to be ready.");
    597             }
    598         }
    599     }
    600 
    601     private boolean isReady() {
    602         try {
    603             return mConnectedSignal.await(0, TimeUnit.MILLISECONDS);
    604         } catch (InterruptedException e) {
    605             return false;
    606         }
    607     }
    608 
    609     private void handleSystemReady() {
    610         // Snapshot current volume states since it's not safe to call into vold
    611         // while holding locks.
    612         final HashMap<String, String> snapshot;
    613         synchronized (mVolumesLock) {
    614             snapshot = new HashMap<String, String>(mVolumeStates);
    615         }
    616 
    617         for (Map.Entry<String, String> entry : snapshot.entrySet()) {
    618             final String path = entry.getKey();
    619             final String state = entry.getValue();
    620 
    621             if (state.equals(Environment.MEDIA_UNMOUNTED)) {
    622                 int rc = doMountVolume(path);
    623                 if (rc != StorageResultCode.OperationSucceeded) {
    624                     Slog.e(TAG, String.format("Boot-time mount failed (%d)",
    625                             rc));
    626                 }
    627             } else if (state.equals(Environment.MEDIA_SHARED)) {
    628                 /*
    629                  * Bootstrap UMS enabled state since vold indicates
    630                  * the volume is shared (runtime restart while ums enabled)
    631                  */
    632                 notifyVolumeStateChange(null, path, VolumeState.NoMedia,
    633                         VolumeState.Shared);
    634             }
    635         }
    636 
    637         // Push mounted state for all emulated storage
    638         synchronized (mVolumesLock) {
    639             for (StorageVolume volume : mVolumes) {
    640                 if (volume.isEmulated()) {
    641                     updatePublicVolumeState(volume, Environment.MEDIA_MOUNTED);
    642                 }
    643             }
    644         }
    645 
    646         /*
    647          * If UMS was connected on boot, send the connected event
    648          * now that we're up.
    649          */
    650         if (mSendUmsConnectedOnBoot) {
    651             sendUmsIntent(true);
    652             mSendUmsConnectedOnBoot = false;
    653         }
    654 
    655         /*
    656          * Start scheduling nominally-daily fstrim operations
    657          */
    658         MountServiceIdler.scheduleIdlePass(mContext);
    659     }
    660 
    661     private final BroadcastReceiver mUserReceiver = new BroadcastReceiver() {
    662         @Override
    663         public void onReceive(Context context, Intent intent) {
    664             final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
    665             if (userId == -1) return;
    666             final UserHandle user = new UserHandle(userId);
    667 
    668             final String action = intent.getAction();
    669             if (Intent.ACTION_USER_ADDED.equals(action)) {
    670                 synchronized (mVolumesLock) {
    671                     createEmulatedVolumeForUserLocked(user);
    672                 }
    673 
    674             } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
    675                 synchronized (mVolumesLock) {
    676                     final List<StorageVolume> toRemove = Lists.newArrayList();
    677                     for (StorageVolume volume : mVolumes) {
    678                         if (user.equals(volume.getOwner())) {
    679                             toRemove.add(volume);
    680                         }
    681                     }
    682                     for (StorageVolume volume : toRemove) {
    683                         removeVolumeLocked(volume);
    684                     }
    685                 }
    686             }
    687         }
    688     };
    689 
    690     private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
    691         @Override
    692         public void onReceive(Context context, Intent intent) {
    693             boolean available = (intent.getBooleanExtra(UsbManager.USB_CONNECTED, false) &&
    694                     intent.getBooleanExtra(UsbManager.USB_FUNCTION_MASS_STORAGE, false));
    695             notifyShareAvailabilityChange(available);
    696         }
    697     };
    698 
    699     private final class MountServiceBinderListener implements IBinder.DeathRecipient {
    700         final IMountServiceListener mListener;
    701 
    702         MountServiceBinderListener(IMountServiceListener listener) {
    703             mListener = listener;
    704 
    705         }
    706 
    707         public void binderDied() {
    708             if (LOCAL_LOGD) Slog.d(TAG, "An IMountServiceListener has died!");
    709             synchronized (mListeners) {
    710                 mListeners.remove(this);
    711                 mListener.asBinder().unlinkToDeath(this, 0);
    712             }
    713         }
    714     }
    715 
    716     void runIdleMaintenance(Runnable callback) {
    717         mHandler.sendMessage(mHandler.obtainMessage(H_FSTRIM, callback));
    718     }
    719 
    720     // Binder entry point for kicking off an immediate fstrim
    721     @Override
    722     public void runMaintenance() {
    723         validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
    724         runIdleMaintenance(null);
    725     }
    726 
    727     @Override
    728     public long lastMaintenance() {
    729         return mLastMaintenance;
    730     }
    731 
    732     private void doShareUnshareVolume(String path, String method, boolean enable) {
    733         // TODO: Add support for multiple share methods
    734         if (!method.equals("ums")) {
    735             throw new IllegalArgumentException(String.format("Method %s not supported", method));
    736         }
    737 
    738         try {
    739             mConnector.execute("volume", enable ? "share" : "unshare", path, method);
    740         } catch (NativeDaemonConnectorException e) {
    741             Slog.e(TAG, "Failed to share/unshare", e);
    742         }
    743     }
    744 
    745     private void updatePublicVolumeState(StorageVolume volume, String state) {
    746         final String path = volume.getPath();
    747         final String oldState;
    748         synchronized (mVolumesLock) {
    749             oldState = mVolumeStates.put(path, state);
    750             volume.setState(state);
    751         }
    752 
    753         if (state.equals(oldState)) {
    754             Slog.w(TAG, String.format("Duplicate state transition (%s -> %s) for %s",
    755                     state, state, path));
    756             return;
    757         }
    758 
    759         Slog.d(TAG, "volume state changed for " + path + " (" + oldState + " -> " + state + ")");
    760 
    761         // Tell PackageManager about changes to primary volume state, but only
    762         // when not emulated.
    763         if (volume.isPrimary() && !volume.isEmulated()) {
    764             if (Environment.MEDIA_UNMOUNTED.equals(state)) {
    765                 mPms.updateExternalMediaStatus(false, false);
    766 
    767                 /*
    768                  * Some OBBs might have been unmounted when this volume was
    769                  * unmounted, so send a message to the handler to let it know to
    770                  * remove those from the list of mounted OBBS.
    771                  */
    772                 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(
    773                         OBB_FLUSH_MOUNT_STATE, path));
    774             } else if (Environment.MEDIA_MOUNTED.equals(state)) {
    775                 mPms.updateExternalMediaStatus(true, false);
    776             }
    777         }
    778 
    779         synchronized (mListeners) {
    780             for (int i = mListeners.size() -1; i >= 0; i--) {
    781                 MountServiceBinderListener bl = mListeners.get(i);
    782                 try {
    783                     bl.mListener.onStorageStateChanged(path, oldState, state);
    784                 } catch (RemoteException rex) {
    785                     Slog.e(TAG, "Listener dead");
    786                     mListeners.remove(i);
    787                 } catch (Exception ex) {
    788                     Slog.e(TAG, "Listener failed", ex);
    789                 }
    790             }
    791         }
    792     }
    793 
    794     /**
    795      * Callback from NativeDaemonConnector
    796      */
    797     public void onDaemonConnected() {
    798         /*
    799          * Since we'll be calling back into the NativeDaemonConnector,
    800          * we need to do our work in a new thread.
    801          */
    802         new Thread("MountService#onDaemonConnected") {
    803             @Override
    804             public void run() {
    805                 /**
    806                  * Determine media state and UMS detection status
    807                  */
    808                 try {
    809                     final String[] vols = NativeDaemonEvent.filterMessageList(
    810                             mConnector.executeForList("volume", "list", "broadcast"),
    811                             VoldResponseCode.VolumeListResult);
    812                     for (String volstr : vols) {
    813                         String[] tok = volstr.split(" ");
    814                         // FMT: <label> <mountpoint> <state>
    815                         String path = tok[1];
    816                         String state = Environment.MEDIA_REMOVED;
    817 
    818                         final StorageVolume volume;
    819                         synchronized (mVolumesLock) {
    820                             volume = mVolumesByPath.get(path);
    821                         }
    822 
    823                         int st = Integer.parseInt(tok[2]);
    824                         if (st == VolumeState.NoMedia) {
    825                             state = Environment.MEDIA_REMOVED;
    826                         } else if (st == VolumeState.Idle) {
    827                             state = Environment.MEDIA_UNMOUNTED;
    828                         } else if (st == VolumeState.Mounted) {
    829                             state = Environment.MEDIA_MOUNTED;
    830                             Slog.i(TAG, "Media already mounted on daemon connection");
    831                         } else if (st == VolumeState.Shared) {
    832                             state = Environment.MEDIA_SHARED;
    833                             Slog.i(TAG, "Media shared on daemon connection");
    834                         } else {
    835                             throw new Exception(String.format("Unexpected state %d", st));
    836                         }
    837 
    838                         if (state != null) {
    839                             if (DEBUG_EVENTS) Slog.i(TAG, "Updating valid state " + state);
    840                             updatePublicVolumeState(volume, state);
    841                         }
    842                     }
    843                 } catch (Exception e) {
    844                     Slog.e(TAG, "Error processing initial volume state", e);
    845                     final StorageVolume primary = getPrimaryPhysicalVolume();
    846                     if (primary != null) {
    847                         updatePublicVolumeState(primary, Environment.MEDIA_REMOVED);
    848                     }
    849                 }
    850 
    851                 /*
    852                  * Now that we've done our initialization, release
    853                  * the hounds!
    854                  */
    855                 mConnectedSignal.countDown();
    856 
    857                 // On an encrypted device we can't see system properties yet, so pull
    858                 // the system locale out of the mount service.
    859                 if ("".equals(SystemProperties.get("vold.encrypt_progress"))) {
    860                     copyLocaleFromMountService();
    861                 }
    862 
    863                 // Let package manager load internal ASECs.
    864                 mPms.scanAvailableAsecs();
    865 
    866                 // Notify people waiting for ASECs to be scanned that it's done.
    867                 mAsecsScanned.countDown();
    868             }
    869         }.start();
    870     }
    871 
    872     private void copyLocaleFromMountService() {
    873         String systemLocale;
    874         try {
    875             systemLocale = getField(StorageManager.SYSTEM_LOCALE_KEY);
    876         } catch (RemoteException e) {
    877             return;
    878         }
    879         if (TextUtils.isEmpty(systemLocale)) {
    880             return;
    881         }
    882 
    883         Slog.d(TAG, "Got locale " + systemLocale + " from mount service");
    884         Locale locale = Locale.forLanguageTag(systemLocale);
    885         Configuration config = new Configuration();
    886         config.setLocale(locale);
    887         try {
    888             ActivityManagerNative.getDefault().updateConfiguration(config);
    889         } catch (RemoteException e) {
    890             Slog.e(TAG, "Error setting system locale from mount service", e);
    891         }
    892 
    893         // Temporary workaround for http://b/17945169.
    894         Slog.d(TAG, "Setting system properties to " + systemLocale + " from mount service");
    895         SystemProperties.set("persist.sys.language", locale.getLanguage());
    896         SystemProperties.set("persist.sys.country", locale.getCountry());
    897     }
    898 
    899     /**
    900      * Callback from NativeDaemonConnector
    901      */
    902     public boolean onCheckHoldWakeLock(int code) {
    903         return false;
    904     }
    905 
    906     /**
    907      * Callback from NativeDaemonConnector
    908      */
    909     public boolean onEvent(int code, String raw, String[] cooked) {
    910         if (DEBUG_EVENTS) {
    911             StringBuilder builder = new StringBuilder();
    912             builder.append("onEvent::");
    913             builder.append(" raw= " + raw);
    914             if (cooked != null) {
    915                 builder.append(" cooked = " );
    916                 for (String str : cooked) {
    917                     builder.append(" " + str);
    918                 }
    919             }
    920             Slog.i(TAG, builder.toString());
    921         }
    922         if (code == VoldResponseCode.VolumeStateChange) {
    923             /*
    924              * One of the volumes we're managing has changed state.
    925              * Format: "NNN Volume <label> <path> state changed
    926              * from <old_#> (<old_str>) to <new_#> (<new_str>)"
    927              */
    928             notifyVolumeStateChange(
    929                     cooked[2], cooked[3], Integer.parseInt(cooked[7]),
    930                             Integer.parseInt(cooked[10]));
    931         } else if (code == VoldResponseCode.VolumeUuidChange) {
    932             // Format: nnn <label> <path> <uuid>
    933             final String path = cooked[2];
    934             final String uuid = (cooked.length > 3) ? cooked[3] : null;
    935 
    936             final StorageVolume vol = mVolumesByPath.get(path);
    937             if (vol != null) {
    938                 vol.setUuid(uuid);
    939             }
    940 
    941         } else if (code == VoldResponseCode.VolumeUserLabelChange) {
    942             // Format: nnn <label> <path> <label>
    943             final String path = cooked[2];
    944             final String userLabel = (cooked.length > 3) ? cooked[3] : null;
    945 
    946             final StorageVolume vol = mVolumesByPath.get(path);
    947             if (vol != null) {
    948                 vol.setUserLabel(userLabel);
    949             }
    950 
    951         } else if ((code == VoldResponseCode.VolumeDiskInserted) ||
    952                    (code == VoldResponseCode.VolumeDiskRemoved) ||
    953                    (code == VoldResponseCode.VolumeBadRemoval)) {
    954             // FMT: NNN Volume <label> <mountpoint> disk inserted (<major>:<minor>)
    955             // FMT: NNN Volume <label> <mountpoint> disk removed (<major>:<minor>)
    956             // FMT: NNN Volume <label> <mountpoint> bad removal (<major>:<minor>)
    957             String action = null;
    958             final String label = cooked[2];
    959             final String path = cooked[3];
    960             int major = -1;
    961             int minor = -1;
    962 
    963             try {
    964                 String devComp = cooked[6].substring(1, cooked[6].length() -1);
    965                 String[] devTok = devComp.split(":");
    966                 major = Integer.parseInt(devTok[0]);
    967                 minor = Integer.parseInt(devTok[1]);
    968             } catch (Exception ex) {
    969                 Slog.e(TAG, "Failed to parse major/minor", ex);
    970             }
    971 
    972             final StorageVolume volume;
    973             final String state;
    974             synchronized (mVolumesLock) {
    975                 volume = mVolumesByPath.get(path);
    976                 state = mVolumeStates.get(path);
    977             }
    978 
    979             if (code == VoldResponseCode.VolumeDiskInserted) {
    980                 new Thread("MountService#VolumeDiskInserted") {
    981                     @Override
    982                     public void run() {
    983                         try {
    984                             int rc;
    985                             if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {
    986                                 Slog.w(TAG, String.format("Insertion mount failed (%d)", rc));
    987                             }
    988                         } catch (Exception ex) {
    989                             Slog.w(TAG, "Failed to mount media on insertion", ex);
    990                         }
    991                     }
    992                 }.start();
    993             } else if (code == VoldResponseCode.VolumeDiskRemoved) {
    994                 /*
    995                  * This event gets trumped if we're already in BAD_REMOVAL state
    996                  */
    997                 if (getVolumeState(path).equals(Environment.MEDIA_BAD_REMOVAL)) {
    998                     return true;
    999                 }
   1000                 /* Send the media unmounted event first */
   1001                 if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
   1002                 updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
   1003                 sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, volume, UserHandle.ALL);
   1004 
   1005                 if (DEBUG_EVENTS) Slog.i(TAG, "Sending media removed");
   1006                 updatePublicVolumeState(volume, Environment.MEDIA_REMOVED);
   1007                 action = Intent.ACTION_MEDIA_REMOVED;
   1008             } else if (code == VoldResponseCode.VolumeBadRemoval) {
   1009                 if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
   1010                 /* Send the media unmounted event first */
   1011                 updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
   1012                 sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, volume, UserHandle.ALL);
   1013 
   1014                 if (DEBUG_EVENTS) Slog.i(TAG, "Sending media bad removal");
   1015                 updatePublicVolumeState(volume, Environment.MEDIA_BAD_REMOVAL);
   1016                 action = Intent.ACTION_MEDIA_BAD_REMOVAL;
   1017             } else if (code == VoldResponseCode.FstrimCompleted) {
   1018                 EventLogTags.writeFstrimFinish(SystemClock.elapsedRealtime());
   1019             } else {
   1020                 Slog.e(TAG, String.format("Unknown code {%d}", code));
   1021             }
   1022 
   1023             if (action != null) {
   1024                 sendStorageIntent(action, volume, UserHandle.ALL);
   1025             }
   1026         } else {
   1027             return false;
   1028         }
   1029 
   1030         return true;
   1031     }
   1032 
   1033     private void notifyVolumeStateChange(String label, String path, int oldState, int newState) {
   1034         final StorageVolume volume;
   1035         final String state;
   1036         synchronized (mVolumesLock) {
   1037             volume = mVolumesByPath.get(path);
   1038             state = getVolumeState(path);
   1039         }
   1040 
   1041         if (DEBUG_EVENTS) Slog.i(TAG, "notifyVolumeStateChange::" + state);
   1042 
   1043         String action = null;
   1044 
   1045         if (oldState == VolumeState.Shared && newState != oldState) {
   1046             if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_UNSHARED intent");
   1047             sendStorageIntent(Intent.ACTION_MEDIA_UNSHARED, volume, UserHandle.ALL);
   1048         }
   1049 
   1050         if (newState == VolumeState.Init) {
   1051         } else if (newState == VolumeState.NoMedia) {
   1052             // NoMedia is handled via Disk Remove events
   1053         } else if (newState == VolumeState.Idle) {
   1054             /*
   1055              * Don't notify if we're in BAD_REMOVAL, NOFS, UNMOUNTABLE, or
   1056              * if we're in the process of enabling UMS
   1057              */
   1058             if (!state.equals(
   1059                     Environment.MEDIA_BAD_REMOVAL) && !state.equals(
   1060                             Environment.MEDIA_NOFS) && !state.equals(
   1061                                     Environment.MEDIA_UNMOUNTABLE) && !getUmsEnabling()) {
   1062                 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state for media bad removal nofs and unmountable");
   1063                 updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
   1064                 action = Intent.ACTION_MEDIA_UNMOUNTED;
   1065             }
   1066         } else if (newState == VolumeState.Pending) {
   1067         } else if (newState == VolumeState.Checking) {
   1068             if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state checking");
   1069             updatePublicVolumeState(volume, Environment.MEDIA_CHECKING);
   1070             action = Intent.ACTION_MEDIA_CHECKING;
   1071         } else if (newState == VolumeState.Mounted) {
   1072             if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state mounted");
   1073             updatePublicVolumeState(volume, Environment.MEDIA_MOUNTED);
   1074             action = Intent.ACTION_MEDIA_MOUNTED;
   1075         } else if (newState == VolumeState.Unmounting) {
   1076             action = Intent.ACTION_MEDIA_EJECT;
   1077         } else if (newState == VolumeState.Formatting) {
   1078         } else if (newState == VolumeState.Shared) {
   1079             if (DEBUG_EVENTS) Slog.i(TAG, "Updating volume state media mounted");
   1080             /* Send the media unmounted event first */
   1081             updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
   1082             sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, volume, UserHandle.ALL);
   1083 
   1084             if (DEBUG_EVENTS) Slog.i(TAG, "Updating media shared");
   1085             updatePublicVolumeState(volume, Environment.MEDIA_SHARED);
   1086             action = Intent.ACTION_MEDIA_SHARED;
   1087             if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_SHARED intent");
   1088         } else if (newState == VolumeState.SharedMnt) {
   1089             Slog.e(TAG, "Live shared mounts not supported yet!");
   1090             return;
   1091         } else {
   1092             Slog.e(TAG, "Unhandled VolumeState {" + newState + "}");
   1093         }
   1094 
   1095         if (action != null) {
   1096             sendStorageIntent(action, volume, UserHandle.ALL);
   1097         }
   1098     }
   1099 
   1100     private int doMountVolume(String path) {
   1101         int rc = StorageResultCode.OperationSucceeded;
   1102 
   1103         final StorageVolume volume;
   1104         synchronized (mVolumesLock) {
   1105             volume = mVolumesByPath.get(path);
   1106         }
   1107 
   1108         if (!volume.isEmulated() && hasUserRestriction(UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA)) {
   1109             Slog.w(TAG, "User has restriction DISALLOW_MOUNT_PHYSICAL_MEDIA; cannot mount volume.");
   1110             return StorageResultCode.OperationFailedInternalError;
   1111         }
   1112 
   1113         if (DEBUG_EVENTS) Slog.i(TAG, "doMountVolume: Mouting " + path);
   1114         try {
   1115             mConnector.execute("volume", "mount", path);
   1116         } catch (NativeDaemonConnectorException e) {
   1117             /*
   1118              * Mount failed for some reason
   1119              */
   1120             String action = null;
   1121             int code = e.getCode();
   1122             if (code == VoldResponseCode.OpFailedNoMedia) {
   1123                 /*
   1124                  * Attempt to mount but no media inserted
   1125                  */
   1126                 rc = StorageResultCode.OperationFailedNoMedia;
   1127             } else if (code == VoldResponseCode.OpFailedMediaBlank) {
   1128                 if (DEBUG_EVENTS) Slog.i(TAG, " updating volume state :: media nofs");
   1129                 /*
   1130                  * Media is blank or does not contain a supported filesystem
   1131                  */
   1132                 updatePublicVolumeState(volume, Environment.MEDIA_NOFS);
   1133                 action = Intent.ACTION_MEDIA_NOFS;
   1134                 rc = StorageResultCode.OperationFailedMediaBlank;
   1135             } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
   1136                 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state media corrupt");
   1137                 /*
   1138                  * Volume consistency check failed
   1139                  */
   1140                 updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTABLE);
   1141                 action = Intent.ACTION_MEDIA_UNMOUNTABLE;
   1142                 rc = StorageResultCode.OperationFailedMediaCorrupt;
   1143             } else {
   1144                 rc = StorageResultCode.OperationFailedInternalError;
   1145             }
   1146 
   1147             /*
   1148              * Send broadcast intent (if required for the failure)
   1149              */
   1150             if (action != null) {
   1151                 sendStorageIntent(action, volume, UserHandle.ALL);
   1152             }
   1153         }
   1154 
   1155         return rc;
   1156     }
   1157 
   1158     /*
   1159      * If force is not set, we do not unmount if there are
   1160      * processes holding references to the volume about to be unmounted.
   1161      * If force is set, all the processes holding references need to be
   1162      * killed via the ActivityManager before actually unmounting the volume.
   1163      * This might even take a while and might be retried after timed delays
   1164      * to make sure we dont end up in an instable state and kill some core
   1165      * processes.
   1166      * If removeEncryption is set, force is implied, and the system will remove any encryption
   1167      * mapping set on the volume when unmounting.
   1168      */
   1169     private int doUnmountVolume(String path, boolean force, boolean removeEncryption) {
   1170         if (!getVolumeState(path).equals(Environment.MEDIA_MOUNTED)) {
   1171             return VoldResponseCode.OpFailedVolNotMounted;
   1172         }
   1173 
   1174         /*
   1175          * Force a GC to make sure AssetManagers in other threads of the
   1176          * system_server are cleaned up. We have to do this since AssetManager
   1177          * instances are kept as a WeakReference and it's possible we have files
   1178          * open on the external storage.
   1179          */
   1180         Runtime.getRuntime().gc();
   1181 
   1182         // Redundant probably. But no harm in updating state again.
   1183         mPms.updateExternalMediaStatus(false, false);
   1184         try {
   1185             final Command cmd = new Command("volume", "unmount", path);
   1186             if (removeEncryption) {
   1187                 cmd.appendArg("force_and_revert");
   1188             } else if (force) {
   1189                 cmd.appendArg("force");
   1190             }
   1191             mConnector.execute(cmd);
   1192             // We unmounted the volume. None of the asec containers are available now.
   1193             synchronized (mAsecMountSet) {
   1194                 mAsecMountSet.clear();
   1195             }
   1196             return StorageResultCode.OperationSucceeded;
   1197         } catch (NativeDaemonConnectorException e) {
   1198             // Don't worry about mismatch in PackageManager since the
   1199             // call back will handle the status changes any way.
   1200             int code = e.getCode();
   1201             if (code == VoldResponseCode.OpFailedVolNotMounted) {
   1202                 return StorageResultCode.OperationFailedStorageNotMounted;
   1203             } else if (code == VoldResponseCode.OpFailedStorageBusy) {
   1204                 return StorageResultCode.OperationFailedStorageBusy;
   1205             } else {
   1206                 return StorageResultCode.OperationFailedInternalError;
   1207             }
   1208         }
   1209     }
   1210 
   1211     private int doFormatVolume(String path) {
   1212         try {
   1213             mConnector.execute("volume", "format", path);
   1214             return StorageResultCode.OperationSucceeded;
   1215         } catch (NativeDaemonConnectorException e) {
   1216             int code = e.getCode();
   1217             if (code == VoldResponseCode.OpFailedNoMedia) {
   1218                 return StorageResultCode.OperationFailedNoMedia;
   1219             } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
   1220                 return StorageResultCode.OperationFailedMediaCorrupt;
   1221             } else {
   1222                 return StorageResultCode.OperationFailedInternalError;
   1223             }
   1224         }
   1225     }
   1226 
   1227     private boolean doGetVolumeShared(String path, String method) {
   1228         final NativeDaemonEvent event;
   1229         try {
   1230             event = mConnector.execute("volume", "shared", path, method);
   1231         } catch (NativeDaemonConnectorException ex) {
   1232             Slog.e(TAG, "Failed to read response to volume shared " + path + " " + method);
   1233             return false;
   1234         }
   1235 
   1236         if (event.getCode() == VoldResponseCode.ShareEnabledResult) {
   1237             return event.getMessage().endsWith("enabled");
   1238         } else {
   1239             return false;
   1240         }
   1241     }
   1242 
   1243     private void notifyShareAvailabilityChange(final boolean avail) {
   1244         synchronized (mListeners) {
   1245             mUmsAvailable = avail;
   1246             for (int i = mListeners.size() -1; i >= 0; i--) {
   1247                 MountServiceBinderListener bl = mListeners.get(i);
   1248                 try {
   1249                     bl.mListener.onUsbMassStorageConnectionChanged(avail);
   1250                 } catch (RemoteException rex) {
   1251                     Slog.e(TAG, "Listener dead");
   1252                     mListeners.remove(i);
   1253                 } catch (Exception ex) {
   1254                     Slog.e(TAG, "Listener failed", ex);
   1255                 }
   1256             }
   1257         }
   1258 
   1259         if (mSystemReady == true) {
   1260             sendUmsIntent(avail);
   1261         } else {
   1262             mSendUmsConnectedOnBoot = avail;
   1263         }
   1264 
   1265         final StorageVolume primary = getPrimaryPhysicalVolume();
   1266         if (avail == false && primary != null
   1267                 && Environment.MEDIA_SHARED.equals(getVolumeState(primary.getPath()))) {
   1268             final String path = primary.getPath();
   1269             /*
   1270              * USB mass storage disconnected while enabled
   1271              */
   1272             new Thread("MountService#AvailabilityChange") {
   1273                 @Override
   1274                 public void run() {
   1275                     try {
   1276                         int rc;
   1277                         Slog.w(TAG, "Disabling UMS after cable disconnect");
   1278                         doShareUnshareVolume(path, "ums", false);
   1279                         if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {
   1280                             Slog.e(TAG, String.format(
   1281                                     "Failed to remount {%s} on UMS enabled-disconnect (%d)",
   1282                                             path, rc));
   1283                         }
   1284                     } catch (Exception ex) {
   1285                         Slog.w(TAG, "Failed to mount media on UMS enabled-disconnect", ex);
   1286                     }
   1287                 }
   1288             }.start();
   1289         }
   1290     }
   1291 
   1292     private void sendStorageIntent(String action, StorageVolume volume, UserHandle user) {
   1293         final Intent intent = new Intent(action, Uri.parse("file://" + volume.getPath()));
   1294         intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, volume);
   1295         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
   1296         Slog.d(TAG, "sendStorageIntent " + intent + " to " + user);
   1297         mContext.sendBroadcastAsUser(intent, user);
   1298     }
   1299 
   1300     private void sendUmsIntent(boolean c) {
   1301         mContext.sendBroadcastAsUser(
   1302                 new Intent((c ? Intent.ACTION_UMS_CONNECTED : Intent.ACTION_UMS_DISCONNECTED)),
   1303                 UserHandle.ALL);
   1304     }
   1305 
   1306     private void validatePermission(String perm) {
   1307         if (mContext.checkCallingOrSelfPermission(perm) != PackageManager.PERMISSION_GRANTED) {
   1308             throw new SecurityException(String.format("Requires %s permission", perm));
   1309         }
   1310     }
   1311 
   1312     private boolean hasUserRestriction(String restriction) {
   1313         UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
   1314         return um.hasUserRestriction(restriction, Binder.getCallingUserHandle());
   1315     }
   1316 
   1317     private void validateUserRestriction(String restriction) {
   1318         if (hasUserRestriction(restriction)) {
   1319             throw new SecurityException("User has restriction " + restriction);
   1320         }
   1321     }
   1322 
   1323     // Storage list XML tags
   1324     private static final String TAG_STORAGE_LIST = "StorageList";
   1325     private static final String TAG_STORAGE = "storage";
   1326 
   1327     private void readStorageListLocked() {
   1328         mVolumes.clear();
   1329         mVolumeStates.clear();
   1330 
   1331         Resources resources = mContext.getResources();
   1332 
   1333         int id = com.android.internal.R.xml.storage_list;
   1334         XmlResourceParser parser = resources.getXml(id);
   1335         AttributeSet attrs = Xml.asAttributeSet(parser);
   1336 
   1337         try {
   1338             XmlUtils.beginDocument(parser, TAG_STORAGE_LIST);
   1339             while (true) {
   1340                 XmlUtils.nextElement(parser);
   1341 
   1342                 String element = parser.getName();
   1343                 if (element == null) break;
   1344 
   1345                 if (TAG_STORAGE.equals(element)) {
   1346                     TypedArray a = resources.obtainAttributes(attrs,
   1347                             com.android.internal.R.styleable.Storage);
   1348 
   1349                     String path = a.getString(
   1350                             com.android.internal.R.styleable.Storage_mountPoint);
   1351                     int descriptionId = a.getResourceId(
   1352                             com.android.internal.R.styleable.Storage_storageDescription, -1);
   1353                     CharSequence description = a.getText(
   1354                             com.android.internal.R.styleable.Storage_storageDescription);
   1355                     boolean primary = a.getBoolean(
   1356                             com.android.internal.R.styleable.Storage_primary, false);
   1357                     boolean removable = a.getBoolean(
   1358                             com.android.internal.R.styleable.Storage_removable, false);
   1359                     boolean emulated = a.getBoolean(
   1360                             com.android.internal.R.styleable.Storage_emulated, false);
   1361                     int mtpReserve = a.getInt(
   1362                             com.android.internal.R.styleable.Storage_mtpReserve, 0);
   1363                     boolean allowMassStorage = a.getBoolean(
   1364                             com.android.internal.R.styleable.Storage_allowMassStorage, false);
   1365                     // resource parser does not support longs, so XML value is in megabytes
   1366                     long maxFileSize = a.getInt(
   1367                             com.android.internal.R.styleable.Storage_maxFileSize, 0) * 1024L * 1024L;
   1368 
   1369                     Slog.d(TAG, "got storage path: " + path + " description: " + description +
   1370                             " primary: " + primary + " removable: " + removable +
   1371                             " emulated: " + emulated +  " mtpReserve: " + mtpReserve +
   1372                             " allowMassStorage: " + allowMassStorage +
   1373                             " maxFileSize: " + maxFileSize);
   1374 
   1375                     if (emulated) {
   1376                         // For devices with emulated storage, we create separate
   1377                         // volumes for each known user.
   1378                         mEmulatedTemplate = new StorageVolume(null, descriptionId, true, false,
   1379                                 true, mtpReserve, false, maxFileSize, null);
   1380 
   1381                         final UserManagerService userManager = UserManagerService.getInstance();
   1382                         for (UserInfo user : userManager.getUsers(false)) {
   1383                             createEmulatedVolumeForUserLocked(user.getUserHandle());
   1384                         }
   1385 
   1386                     } else {
   1387                         if (path == null || description == null) {
   1388                             Slog.e(TAG, "Missing storage path or description in readStorageList");
   1389                         } else {
   1390                             final StorageVolume volume = new StorageVolume(new File(path),
   1391                                     descriptionId, primary, removable, emulated, mtpReserve,
   1392                                     allowMassStorage, maxFileSize, null);
   1393                             addVolumeLocked(volume);
   1394 
   1395                             // Until we hear otherwise, treat as unmounted
   1396                             mVolumeStates.put(volume.getPath(), Environment.MEDIA_UNMOUNTED);
   1397                             volume.setState(Environment.MEDIA_UNMOUNTED);
   1398                         }
   1399                     }
   1400 
   1401                     a.recycle();
   1402                 }
   1403             }
   1404         } catch (XmlPullParserException e) {
   1405             throw new RuntimeException(e);
   1406         } catch (IOException e) {
   1407             throw new RuntimeException(e);
   1408         } finally {
   1409             // Compute storage ID for each physical volume; emulated storage is
   1410             // always 0 when defined.
   1411             int index = isExternalStorageEmulated() ? 1 : 0;
   1412             for (StorageVolume volume : mVolumes) {
   1413                 if (!volume.isEmulated()) {
   1414                     volume.setStorageId(index++);
   1415                 }
   1416             }
   1417             parser.close();
   1418         }
   1419     }
   1420 
   1421     /**
   1422      * Create and add new {@link StorageVolume} for given {@link UserHandle}
   1423      * using {@link #mEmulatedTemplate} as template.
   1424      */
   1425     private void createEmulatedVolumeForUserLocked(UserHandle user) {
   1426         if (mEmulatedTemplate == null) {
   1427             throw new IllegalStateException("Missing emulated volume multi-user template");
   1428         }
   1429 
   1430         final UserEnvironment userEnv = new UserEnvironment(user.getIdentifier());
   1431         final File path = userEnv.getExternalStorageDirectory();
   1432         final StorageVolume volume = StorageVolume.fromTemplate(mEmulatedTemplate, path, user);
   1433         volume.setStorageId(0);
   1434         addVolumeLocked(volume);
   1435 
   1436         if (mSystemReady) {
   1437             updatePublicVolumeState(volume, Environment.MEDIA_MOUNTED);
   1438         } else {
   1439             // Place stub status for early callers to find
   1440             mVolumeStates.put(volume.getPath(), Environment.MEDIA_MOUNTED);
   1441             volume.setState(Environment.MEDIA_MOUNTED);
   1442         }
   1443     }
   1444 
   1445     private void addVolumeLocked(StorageVolume volume) {
   1446         Slog.d(TAG, "addVolumeLocked() " + volume);
   1447         mVolumes.add(volume);
   1448         final StorageVolume existing = mVolumesByPath.put(volume.getPath(), volume);
   1449         if (existing != null) {
   1450             throw new IllegalStateException(
   1451                     "Volume at " + volume.getPath() + " already exists: " + existing);
   1452         }
   1453     }
   1454 
   1455     private void removeVolumeLocked(StorageVolume volume) {
   1456         Slog.d(TAG, "removeVolumeLocked() " + volume);
   1457         mVolumes.remove(volume);
   1458         mVolumesByPath.remove(volume.getPath());
   1459         mVolumeStates.remove(volume.getPath());
   1460     }
   1461 
   1462     private StorageVolume getPrimaryPhysicalVolume() {
   1463         synchronized (mVolumesLock) {
   1464             for (StorageVolume volume : mVolumes) {
   1465                 if (volume.isPrimary() && !volume.isEmulated()) {
   1466                     return volume;
   1467                 }
   1468             }
   1469         }
   1470         return null;
   1471     }
   1472 
   1473     /**
   1474      * Constructs a new MountService instance
   1475      *
   1476      * @param context  Binder context for this service
   1477      */
   1478     public MountService(Context context) {
   1479         sSelf = this;
   1480 
   1481         mContext = context;
   1482 
   1483         synchronized (mVolumesLock) {
   1484             readStorageListLocked();
   1485         }
   1486 
   1487         // XXX: This will go away soon in favor of IMountServiceObserver
   1488         mPms = (PackageManagerService) ServiceManager.getService("package");
   1489 
   1490         HandlerThread hthread = new HandlerThread(TAG);
   1491         hthread.start();
   1492         mHandler = new MountServiceHandler(hthread.getLooper());
   1493 
   1494         // Watch for user changes
   1495         final IntentFilter userFilter = new IntentFilter();
   1496         userFilter.addAction(Intent.ACTION_USER_ADDED);
   1497         userFilter.addAction(Intent.ACTION_USER_REMOVED);
   1498         mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);
   1499 
   1500         // Watch for USB changes on primary volume
   1501         final StorageVolume primary = getPrimaryPhysicalVolume();
   1502         if (primary != null && primary.allowMassStorage()) {
   1503             mContext.registerReceiver(
   1504                     mUsbReceiver, new IntentFilter(UsbManager.ACTION_USB_STATE), null, mHandler);
   1505         }
   1506 
   1507         // Add OBB Action Handler to MountService thread.
   1508         mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper());
   1509 
   1510         // Initialize the last-fstrim tracking if necessary
   1511         File dataDir = Environment.getDataDirectory();
   1512         File systemDir = new File(dataDir, "system");
   1513         mLastMaintenanceFile = new File(systemDir, LAST_FSTRIM_FILE);
   1514         if (!mLastMaintenanceFile.exists()) {
   1515             // Not setting mLastMaintenance here means that we will force an
   1516             // fstrim during reboot following the OTA that installs this code.
   1517             try {
   1518                 (new FileOutputStream(mLastMaintenanceFile)).close();
   1519             } catch (IOException e) {
   1520                 Slog.e(TAG, "Unable to create fstrim record " + mLastMaintenanceFile.getPath());
   1521             }
   1522         } else {
   1523             mLastMaintenance = mLastMaintenanceFile.lastModified();
   1524         }
   1525 
   1526         /*
   1527          * Create the connection to vold with a maximum queue of twice the
   1528          * amount of containers we'd ever expect to have. This keeps an
   1529          * "asec list" from blocking a thread repeatedly.
   1530          */
   1531         mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25,
   1532                 null);
   1533 
   1534         Thread thread = new Thread(mConnector, VOLD_TAG);
   1535         thread.start();
   1536 
   1537         // Add ourself to the Watchdog monitors if enabled.
   1538         if (WATCHDOG_ENABLE) {
   1539             Watchdog.getInstance().addMonitor(this);
   1540         }
   1541     }
   1542 
   1543     public void systemReady() {
   1544         mSystemReady = true;
   1545         mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget();
   1546     }
   1547 
   1548     /**
   1549      * Exposed API calls below here
   1550      */
   1551 
   1552     public void registerListener(IMountServiceListener listener) {
   1553         synchronized (mListeners) {
   1554             MountServiceBinderListener bl = new MountServiceBinderListener(listener);
   1555             try {
   1556                 listener.asBinder().linkToDeath(bl, 0);
   1557                 mListeners.add(bl);
   1558             } catch (RemoteException rex) {
   1559                 Slog.e(TAG, "Failed to link to listener death");
   1560             }
   1561         }
   1562     }
   1563 
   1564     public void unregisterListener(IMountServiceListener listener) {
   1565         synchronized (mListeners) {
   1566             for(MountServiceBinderListener bl : mListeners) {
   1567                 if (bl.mListener.asBinder() == listener.asBinder()) {
   1568                     mListeners.remove(mListeners.indexOf(bl));
   1569                     listener.asBinder().unlinkToDeath(bl, 0);
   1570                     return;
   1571                 }
   1572             }
   1573         }
   1574     }
   1575 
   1576     public void shutdown(final IMountShutdownObserver observer) {
   1577         validatePermission(android.Manifest.permission.SHUTDOWN);
   1578 
   1579         Slog.i(TAG, "Shutting down");
   1580         synchronized (mVolumesLock) {
   1581             // Get all volumes to be unmounted.
   1582             MountShutdownLatch mountShutdownLatch = new MountShutdownLatch(observer,
   1583                                                             mVolumeStates.size());
   1584 
   1585             for (String path : mVolumeStates.keySet()) {
   1586                 String state = mVolumeStates.get(path);
   1587 
   1588                 if (state.equals(Environment.MEDIA_SHARED)) {
   1589                     /*
   1590                      * If the media is currently shared, unshare it.
   1591                      * XXX: This is still dangerous!. We should not
   1592                      * be rebooting at *all* if UMS is enabled, since
   1593                      * the UMS host could have dirty FAT cache entries
   1594                      * yet to flush.
   1595                      */
   1596                     setUsbMassStorageEnabled(false);
   1597                 } else if (state.equals(Environment.MEDIA_CHECKING)) {
   1598                     /*
   1599                      * If the media is being checked, then we need to wait for
   1600                      * it to complete before being able to proceed.
   1601                      */
   1602                     // XXX: @hackbod - Should we disable the ANR timer here?
   1603                     int retries = 30;
   1604                     while (state.equals(Environment.MEDIA_CHECKING) && (retries-- >=0)) {
   1605                         try {
   1606                             Thread.sleep(1000);
   1607                         } catch (InterruptedException iex) {
   1608                             Slog.e(TAG, "Interrupted while waiting for media", iex);
   1609                             break;
   1610                         }
   1611                         state = Environment.getExternalStorageState();
   1612                     }
   1613                     if (retries == 0) {
   1614                         Slog.e(TAG, "Timed out waiting for media to check");
   1615                     }
   1616                 }
   1617 
   1618                 if (state.equals(Environment.MEDIA_MOUNTED)) {
   1619                     // Post a unmount message.
   1620                     ShutdownCallBack ucb = new ShutdownCallBack(path, mountShutdownLatch);
   1621                     mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
   1622                 } else if (observer != null) {
   1623                     /*
   1624                      * Count down, since nothing will be done. The observer will be
   1625                      * notified when we are done so shutdown sequence can continue.
   1626                      */
   1627                     mountShutdownLatch.countDown();
   1628                     Slog.i(TAG, "Unmount completed: " + path +
   1629                         ", result code: " + StorageResultCode.OperationSucceeded);
   1630                 }
   1631             }
   1632         }
   1633     }
   1634 
   1635     private boolean getUmsEnabling() {
   1636         synchronized (mListeners) {
   1637             return mUmsEnabling;
   1638         }
   1639     }
   1640 
   1641     private void setUmsEnabling(boolean enable) {
   1642         synchronized (mListeners) {
   1643             mUmsEnabling = enable;
   1644         }
   1645     }
   1646 
   1647     public boolean isUsbMassStorageConnected() {
   1648         waitForReady();
   1649 
   1650         if (getUmsEnabling()) {
   1651             return true;
   1652         }
   1653         synchronized (mListeners) {
   1654             return mUmsAvailable;
   1655         }
   1656     }
   1657 
   1658     public void setUsbMassStorageEnabled(boolean enable) {
   1659         waitForReady();
   1660         validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
   1661         validateUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER);
   1662 
   1663         final StorageVolume primary = getPrimaryPhysicalVolume();
   1664         if (primary == null) return;
   1665 
   1666         // TODO: Add support for multiple share methods
   1667 
   1668         /*
   1669          * If the volume is mounted and we're enabling then unmount it
   1670          */
   1671         String path = primary.getPath();
   1672         String vs = getVolumeState(path);
   1673         String method = "ums";
   1674         if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
   1675             // Override for isUsbMassStorageEnabled()
   1676             setUmsEnabling(enable);
   1677             UmsEnableCallBack umscb = new UmsEnableCallBack(path, method, true);
   1678             mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, umscb));
   1679             // Clear override
   1680             setUmsEnabling(false);
   1681         }
   1682         /*
   1683          * If we disabled UMS then mount the volume
   1684          */
   1685         if (!enable) {
   1686             doShareUnshareVolume(path, method, enable);
   1687             if (doMountVolume(path) != StorageResultCode.OperationSucceeded) {
   1688                 Slog.e(TAG, "Failed to remount " + path +
   1689                         " after disabling share method " + method);
   1690                 /*
   1691                  * Even though the mount failed, the unshare didn't so don't indicate an error.
   1692                  * The mountVolume() call will have set the storage state and sent the necessary
   1693                  * broadcasts.
   1694                  */
   1695             }
   1696         }
   1697     }
   1698 
   1699     public boolean isUsbMassStorageEnabled() {
   1700         waitForReady();
   1701 
   1702         final StorageVolume primary = getPrimaryPhysicalVolume();
   1703         if (primary != null) {
   1704             return doGetVolumeShared(primary.getPath(), "ums");
   1705         } else {
   1706             return false;
   1707         }
   1708     }
   1709 
   1710     /**
   1711      * @return state of the volume at the specified mount point
   1712      */
   1713     public String getVolumeState(String mountPoint) {
   1714         synchronized (mVolumesLock) {
   1715             String state = mVolumeStates.get(mountPoint);
   1716             if (state == null) {
   1717                 Slog.w(TAG, "getVolumeState(" + mountPoint + "): Unknown volume");
   1718                 if (SystemProperties.get("vold.encrypt_progress").length() != 0) {
   1719                     state = Environment.MEDIA_REMOVED;
   1720                 } else {
   1721                     throw new IllegalArgumentException();
   1722                 }
   1723             }
   1724 
   1725             return state;
   1726         }
   1727     }
   1728 
   1729     @Override
   1730     public boolean isExternalStorageEmulated() {
   1731         return mEmulatedTemplate != null;
   1732     }
   1733 
   1734     public int mountVolume(String path) {
   1735         validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
   1736         waitForReady();
   1737         return doMountVolume(path);
   1738     }
   1739 
   1740     public void unmountVolume(String path, boolean force, boolean removeEncryption) {
   1741         validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
   1742         waitForReady();
   1743 
   1744         String volState = getVolumeState(path);
   1745         if (DEBUG_UNMOUNT) {
   1746             Slog.i(TAG, "Unmounting " + path
   1747                     + " force = " + force
   1748                     + " removeEncryption = " + removeEncryption);
   1749         }
   1750         if (Environment.MEDIA_UNMOUNTED.equals(volState) ||
   1751                 Environment.MEDIA_REMOVED.equals(volState) ||
   1752                 Environment.MEDIA_SHARED.equals(volState) ||
   1753                 Environment.MEDIA_UNMOUNTABLE.equals(volState)) {
   1754             // Media already unmounted or cannot be unmounted.
   1755             // TODO return valid return code when adding observer call back.
   1756             return;
   1757         }
   1758         UnmountCallBack ucb = new UnmountCallBack(path, force, removeEncryption);
   1759         mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
   1760     }
   1761 
   1762     public int formatVolume(String path) {
   1763         validatePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
   1764         waitForReady();
   1765 
   1766         return doFormatVolume(path);
   1767     }
   1768 
   1769     public int[] getStorageUsers(String path) {
   1770         validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
   1771         waitForReady();
   1772         try {
   1773             final String[] r = NativeDaemonEvent.filterMessageList(
   1774                     mConnector.executeForList("storage", "users", path),
   1775                     VoldResponseCode.StorageUsersListResult);
   1776 
   1777             // FMT: <pid> <process name>
   1778             int[] data = new int[r.length];
   1779             for (int i = 0; i < r.length; i++) {
   1780                 String[] tok = r[i].split(" ");
   1781                 try {
   1782                     data[i] = Integer.parseInt(tok[0]);
   1783                 } catch (NumberFormatException nfe) {
   1784                     Slog.e(TAG, String.format("Error parsing pid %s", tok[0]));
   1785                     return new int[0];
   1786                 }
   1787             }
   1788             return data;
   1789         } catch (NativeDaemonConnectorException e) {
   1790             Slog.e(TAG, "Failed to retrieve storage users list", e);
   1791             return new int[0];
   1792         }
   1793     }
   1794 
   1795     private void warnOnNotMounted() {
   1796         final StorageVolume primary = getPrimaryPhysicalVolume();
   1797         if (primary != null) {
   1798             boolean mounted = false;
   1799             try {
   1800                 mounted = Environment.MEDIA_MOUNTED.equals(getVolumeState(primary.getPath()));
   1801             } catch (IllegalArgumentException e) {
   1802             }
   1803 
   1804             if (!mounted) {
   1805                 Slog.w(TAG, "getSecureContainerList() called when storage not mounted");
   1806             }
   1807         }
   1808     }
   1809 
   1810     public String[] getSecureContainerList() {
   1811         validatePermission(android.Manifest.permission.ASEC_ACCESS);
   1812         waitForReady();
   1813         warnOnNotMounted();
   1814 
   1815         try {
   1816             return NativeDaemonEvent.filterMessageList(
   1817                     mConnector.executeForList("asec", "list"), VoldResponseCode.AsecListResult);
   1818         } catch (NativeDaemonConnectorException e) {
   1819             return new String[0];
   1820         }
   1821     }
   1822 
   1823     public int createSecureContainer(String id, int sizeMb, String fstype, String key,
   1824             int ownerUid, boolean external) {
   1825         validatePermission(android.Manifest.permission.ASEC_CREATE);
   1826         waitForReady();
   1827         warnOnNotMounted();
   1828 
   1829         int rc = StorageResultCode.OperationSucceeded;
   1830         try {
   1831             mConnector.execute("asec", "create", id, sizeMb, fstype, new SensitiveArg(key),
   1832                     ownerUid, external ? "1" : "0");
   1833         } catch (NativeDaemonConnectorException e) {
   1834             rc = StorageResultCode.OperationFailedInternalError;
   1835         }
   1836 
   1837         if (rc == StorageResultCode.OperationSucceeded) {
   1838             synchronized (mAsecMountSet) {
   1839                 mAsecMountSet.add(id);
   1840             }
   1841         }
   1842         return rc;
   1843     }
   1844 
   1845     @Override
   1846     public int resizeSecureContainer(String id, int sizeMb, String key) {
   1847         validatePermission(android.Manifest.permission.ASEC_CREATE);
   1848         waitForReady();
   1849         warnOnNotMounted();
   1850 
   1851         int rc = StorageResultCode.OperationSucceeded;
   1852         try {
   1853             mConnector.execute("asec", "resize", id, sizeMb, new SensitiveArg(key));
   1854         } catch (NativeDaemonConnectorException e) {
   1855             rc = StorageResultCode.OperationFailedInternalError;
   1856         }
   1857         return rc;
   1858     }
   1859 
   1860     public int finalizeSecureContainer(String id) {
   1861         validatePermission(android.Manifest.permission.ASEC_CREATE);
   1862         warnOnNotMounted();
   1863 
   1864         int rc = StorageResultCode.OperationSucceeded;
   1865         try {
   1866             mConnector.execute("asec", "finalize", id);
   1867             /*
   1868              * Finalization does a remount, so no need
   1869              * to update mAsecMountSet
   1870              */
   1871         } catch (NativeDaemonConnectorException e) {
   1872             rc = StorageResultCode.OperationFailedInternalError;
   1873         }
   1874         return rc;
   1875     }
   1876 
   1877     public int fixPermissionsSecureContainer(String id, int gid, String filename) {
   1878         validatePermission(android.Manifest.permission.ASEC_CREATE);
   1879         warnOnNotMounted();
   1880 
   1881         int rc = StorageResultCode.OperationSucceeded;
   1882         try {
   1883             mConnector.execute("asec", "fixperms", id, gid, filename);
   1884             /*
   1885              * Fix permissions does a remount, so no need to update
   1886              * mAsecMountSet
   1887              */
   1888         } catch (NativeDaemonConnectorException e) {
   1889             rc = StorageResultCode.OperationFailedInternalError;
   1890         }
   1891         return rc;
   1892     }
   1893 
   1894     public int destroySecureContainer(String id, boolean force) {
   1895         validatePermission(android.Manifest.permission.ASEC_DESTROY);
   1896         waitForReady();
   1897         warnOnNotMounted();
   1898 
   1899         /*
   1900          * Force a GC to make sure AssetManagers in other threads of the
   1901          * system_server are cleaned up. We have to do this since AssetManager
   1902          * instances are kept as a WeakReference and it's possible we have files
   1903          * open on the external storage.
   1904          */
   1905         Runtime.getRuntime().gc();
   1906 
   1907         int rc = StorageResultCode.OperationSucceeded;
   1908         try {
   1909             final Command cmd = new Command("asec", "destroy", id);
   1910             if (force) {
   1911                 cmd.appendArg("force");
   1912             }
   1913             mConnector.execute(cmd);
   1914         } catch (NativeDaemonConnectorException e) {
   1915             int code = e.getCode();
   1916             if (code == VoldResponseCode.OpFailedStorageBusy) {
   1917                 rc = StorageResultCode.OperationFailedStorageBusy;
   1918             } else {
   1919                 rc = StorageResultCode.OperationFailedInternalError;
   1920             }
   1921         }
   1922 
   1923         if (rc == StorageResultCode.OperationSucceeded) {
   1924             synchronized (mAsecMountSet) {
   1925                 if (mAsecMountSet.contains(id)) {
   1926                     mAsecMountSet.remove(id);
   1927                 }
   1928             }
   1929         }
   1930 
   1931         return rc;
   1932     }
   1933 
   1934     public int mountSecureContainer(String id, String key, int ownerUid, boolean readOnly) {
   1935         validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
   1936         waitForReady();
   1937         warnOnNotMounted();
   1938 
   1939         synchronized (mAsecMountSet) {
   1940             if (mAsecMountSet.contains(id)) {
   1941                 return StorageResultCode.OperationFailedStorageMounted;
   1942             }
   1943         }
   1944 
   1945         int rc = StorageResultCode.OperationSucceeded;
   1946         try {
   1947             mConnector.execute("asec", "mount", id, new SensitiveArg(key), ownerUid,
   1948                     readOnly ? "ro" : "rw");
   1949         } catch (NativeDaemonConnectorException e) {
   1950             int code = e.getCode();
   1951             if (code != VoldResponseCode.OpFailedStorageBusy) {
   1952                 rc = StorageResultCode.OperationFailedInternalError;
   1953             }
   1954         }
   1955 
   1956         if (rc == StorageResultCode.OperationSucceeded) {
   1957             synchronized (mAsecMountSet) {
   1958                 mAsecMountSet.add(id);
   1959             }
   1960         }
   1961         return rc;
   1962     }
   1963 
   1964     public int unmountSecureContainer(String id, boolean force) {
   1965         validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
   1966         waitForReady();
   1967         warnOnNotMounted();
   1968 
   1969         synchronized (mAsecMountSet) {
   1970             if (!mAsecMountSet.contains(id)) {
   1971                 return StorageResultCode.OperationFailedStorageNotMounted;
   1972             }
   1973          }
   1974 
   1975         /*
   1976          * Force a GC to make sure AssetManagers in other threads of the
   1977          * system_server are cleaned up. We have to do this since AssetManager
   1978          * instances are kept as a WeakReference and it's possible we have files
   1979          * open on the external storage.
   1980          */
   1981         Runtime.getRuntime().gc();
   1982 
   1983         int rc = StorageResultCode.OperationSucceeded;
   1984         try {
   1985             final Command cmd = new Command("asec", "unmount", id);
   1986             if (force) {
   1987                 cmd.appendArg("force");
   1988             }
   1989             mConnector.execute(cmd);
   1990         } catch (NativeDaemonConnectorException e) {
   1991             int code = e.getCode();
   1992             if (code == VoldResponseCode.OpFailedStorageBusy) {
   1993                 rc = StorageResultCode.OperationFailedStorageBusy;
   1994             } else {
   1995                 rc = StorageResultCode.OperationFailedInternalError;
   1996             }
   1997         }
   1998 
   1999         if (rc == StorageResultCode.OperationSucceeded) {
   2000             synchronized (mAsecMountSet) {
   2001                 mAsecMountSet.remove(id);
   2002             }
   2003         }
   2004         return rc;
   2005     }
   2006 
   2007     public boolean isSecureContainerMounted(String id) {
   2008         validatePermission(android.Manifest.permission.ASEC_ACCESS);
   2009         waitForReady();
   2010         warnOnNotMounted();
   2011 
   2012         synchronized (mAsecMountSet) {
   2013             return mAsecMountSet.contains(id);
   2014         }
   2015     }
   2016 
   2017     public int renameSecureContainer(String oldId, String newId) {
   2018         validatePermission(android.Manifest.permission.ASEC_RENAME);
   2019         waitForReady();
   2020         warnOnNotMounted();
   2021 
   2022         synchronized (mAsecMountSet) {
   2023             /*
   2024              * Because a mounted container has active internal state which cannot be
   2025              * changed while active, we must ensure both ids are not currently mounted.
   2026              */
   2027             if (mAsecMountSet.contains(oldId) || mAsecMountSet.contains(newId)) {
   2028                 return StorageResultCode.OperationFailedStorageMounted;
   2029             }
   2030         }
   2031 
   2032         int rc = StorageResultCode.OperationSucceeded;
   2033         try {
   2034             mConnector.execute("asec", "rename", oldId, newId);
   2035         } catch (NativeDaemonConnectorException e) {
   2036             rc = StorageResultCode.OperationFailedInternalError;
   2037         }
   2038 
   2039         return rc;
   2040     }
   2041 
   2042     public String getSecureContainerPath(String id) {
   2043         validatePermission(android.Manifest.permission.ASEC_ACCESS);
   2044         waitForReady();
   2045         warnOnNotMounted();
   2046 
   2047         final NativeDaemonEvent event;
   2048         try {
   2049             event = mConnector.execute("asec", "path", id);
   2050             event.checkCode(VoldResponseCode.AsecPathResult);
   2051             return event.getMessage();
   2052         } catch (NativeDaemonConnectorException e) {
   2053             int code = e.getCode();
   2054             if (code == VoldResponseCode.OpFailedStorageNotFound) {
   2055                 Slog.i(TAG, String.format("Container '%s' not found", id));
   2056                 return null;
   2057             } else {
   2058                 throw new IllegalStateException(String.format("Unexpected response code %d", code));
   2059             }
   2060         }
   2061     }
   2062 
   2063     public String getSecureContainerFilesystemPath(String id) {
   2064         validatePermission(android.Manifest.permission.ASEC_ACCESS);
   2065         waitForReady();
   2066         warnOnNotMounted();
   2067 
   2068         final NativeDaemonEvent event;
   2069         try {
   2070             event = mConnector.execute("asec", "fspath", id);
   2071             event.checkCode(VoldResponseCode.AsecPathResult);
   2072             return event.getMessage();
   2073         } catch (NativeDaemonConnectorException e) {
   2074             int code = e.getCode();
   2075             if (code == VoldResponseCode.OpFailedStorageNotFound) {
   2076                 Slog.i(TAG, String.format("Container '%s' not found", id));
   2077                 return null;
   2078             } else {
   2079                 throw new IllegalStateException(String.format("Unexpected response code %d", code));
   2080             }
   2081         }
   2082     }
   2083 
   2084     public void finishMediaUpdate() {
   2085         mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
   2086     }
   2087 
   2088     private boolean isUidOwnerOfPackageOrSystem(String packageName, int callerUid) {
   2089         if (callerUid == android.os.Process.SYSTEM_UID) {
   2090             return true;
   2091         }
   2092 
   2093         if (packageName == null) {
   2094             return false;
   2095         }
   2096 
   2097         final int packageUid = mPms.getPackageUid(packageName, UserHandle.getUserId(callerUid));
   2098 
   2099         if (DEBUG_OBB) {
   2100             Slog.d(TAG, "packageName = " + packageName + ", packageUid = " +
   2101                     packageUid + ", callerUid = " + callerUid);
   2102         }
   2103 
   2104         return callerUid == packageUid;
   2105     }
   2106 
   2107     public String getMountedObbPath(String rawPath) {
   2108         Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
   2109 
   2110         waitForReady();
   2111         warnOnNotMounted();
   2112 
   2113         final ObbState state;
   2114         synchronized (mObbPathToStateMap) {
   2115             state = mObbPathToStateMap.get(rawPath);
   2116         }
   2117         if (state == null) {
   2118             Slog.w(TAG, "Failed to find OBB mounted at " + rawPath);
   2119             return null;
   2120         }
   2121 
   2122         final NativeDaemonEvent event;
   2123         try {
   2124             event = mConnector.execute("obb", "path", state.voldPath);
   2125             event.checkCode(VoldResponseCode.AsecPathResult);
   2126             return event.getMessage();
   2127         } catch (NativeDaemonConnectorException e) {
   2128             int code = e.getCode();
   2129             if (code == VoldResponseCode.OpFailedStorageNotFound) {
   2130                 return null;
   2131             } else {
   2132                 throw new IllegalStateException(String.format("Unexpected response code %d", code));
   2133             }
   2134         }
   2135     }
   2136 
   2137     @Override
   2138     public boolean isObbMounted(String rawPath) {
   2139         Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
   2140         synchronized (mObbMounts) {
   2141             return mObbPathToStateMap.containsKey(rawPath);
   2142         }
   2143     }
   2144 
   2145     @Override
   2146     public void mountObb(
   2147             String rawPath, String canonicalPath, String key, IObbActionListener token, int nonce) {
   2148         Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
   2149         Preconditions.checkNotNull(canonicalPath, "canonicalPath cannot be null");
   2150         Preconditions.checkNotNull(token, "token cannot be null");
   2151 
   2152         final int callingUid = Binder.getCallingUid();
   2153         final ObbState obbState = new ObbState(rawPath, canonicalPath, callingUid, token, nonce);
   2154         final ObbAction action = new MountObbAction(obbState, key, callingUid);
   2155         mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
   2156 
   2157         if (DEBUG_OBB)
   2158             Slog.i(TAG, "Send to OBB handler: " + action.toString());
   2159     }
   2160 
   2161     @Override
   2162     public void unmountObb(String rawPath, boolean force, IObbActionListener token, int nonce) {
   2163         Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
   2164 
   2165         final ObbState existingState;
   2166         synchronized (mObbPathToStateMap) {
   2167             existingState = mObbPathToStateMap.get(rawPath);
   2168         }
   2169 
   2170         if (existingState != null) {
   2171             // TODO: separate state object from request data
   2172             final int callingUid = Binder.getCallingUid();
   2173             final ObbState newState = new ObbState(
   2174                     rawPath, existingState.canonicalPath, callingUid, token, nonce);
   2175             final ObbAction action = new UnmountObbAction(newState, force);
   2176             mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
   2177 
   2178             if (DEBUG_OBB)
   2179                 Slog.i(TAG, "Send to OBB handler: " + action.toString());
   2180         } else {
   2181             Slog.w(TAG, "Unknown OBB mount at " + rawPath);
   2182         }
   2183     }
   2184 
   2185     @Override
   2186     public int getEncryptionState() {
   2187         mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
   2188                 "no permission to access the crypt keeper");
   2189 
   2190         waitForReady();
   2191 
   2192         final NativeDaemonEvent event;
   2193         try {
   2194             event = mConnector.execute("cryptfs", "cryptocomplete");
   2195             return Integer.parseInt(event.getMessage());
   2196         } catch (NumberFormatException e) {
   2197             // Bad result - unexpected.
   2198             Slog.w(TAG, "Unable to parse result from cryptfs cryptocomplete");
   2199             return ENCRYPTION_STATE_ERROR_UNKNOWN;
   2200         } catch (NativeDaemonConnectorException e) {
   2201             // Something bad happened.
   2202             Slog.w(TAG, "Error in communicating with cryptfs in validating");
   2203             return ENCRYPTION_STATE_ERROR_UNKNOWN;
   2204         }
   2205     }
   2206 
   2207     private String toHex(String password) {
   2208         if (password == null) {
   2209             return new String();
   2210         }
   2211         byte[] bytes = password.getBytes(StandardCharsets.UTF_8);
   2212         return new String(Hex.encodeHex(bytes));
   2213     }
   2214 
   2215     private String fromHex(String hexPassword) {
   2216         if (hexPassword == null) {
   2217             return null;
   2218         }
   2219 
   2220         try {
   2221             byte[] bytes = Hex.decodeHex(hexPassword.toCharArray());
   2222             return new String(bytes, StandardCharsets.UTF_8);
   2223         } catch (DecoderException e) {
   2224             return null;
   2225         }
   2226     }
   2227 
   2228     @Override
   2229     public int decryptStorage(String password) {
   2230         if (TextUtils.isEmpty(password)) {
   2231             throw new IllegalArgumentException("password cannot be empty");
   2232         }
   2233 
   2234         mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
   2235                 "no permission to access the crypt keeper");
   2236 
   2237         waitForReady();
   2238 
   2239         if (DEBUG_EVENTS) {
   2240             Slog.i(TAG, "decrypting storage...");
   2241         }
   2242 
   2243         final NativeDaemonEvent event;
   2244         try {
   2245             event = mConnector.execute("cryptfs", "checkpw", new SensitiveArg(toHex(password)));
   2246 
   2247             final int code = Integer.parseInt(event.getMessage());
   2248             if (code == 0) {
   2249                 // Decrypt was successful. Post a delayed message before restarting in order
   2250                 // to let the UI to clear itself
   2251                 mHandler.postDelayed(new Runnable() {
   2252                     public void run() {
   2253                         try {
   2254                             mConnector.execute("cryptfs", "restart");
   2255                         } catch (NativeDaemonConnectorException e) {
   2256                             Slog.e(TAG, "problem executing in background", e);
   2257                         }
   2258                     }
   2259                 }, 1000); // 1 second
   2260             }
   2261 
   2262             return code;
   2263         } catch (NativeDaemonConnectorException e) {
   2264             // Decryption failed
   2265             return e.getCode();
   2266         }
   2267     }
   2268 
   2269     public int encryptStorage(int type, String password) {
   2270         if (TextUtils.isEmpty(password) && type != StorageManager.CRYPT_TYPE_DEFAULT) {
   2271             throw new IllegalArgumentException("password cannot be empty");
   2272         }
   2273 
   2274         mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
   2275             "no permission to access the crypt keeper");
   2276 
   2277         waitForReady();
   2278 
   2279         if (DEBUG_EVENTS) {
   2280             Slog.i(TAG, "encrypting storage...");
   2281         }
   2282 
   2283         try {
   2284             mConnector.execute("cryptfs", "enablecrypto", "inplace", CRYPTO_TYPES[type],
   2285                                new SensitiveArg(toHex(password)));
   2286         } catch (NativeDaemonConnectorException e) {
   2287             // Encryption failed
   2288             return e.getCode();
   2289         }
   2290 
   2291         return 0;
   2292     }
   2293 
   2294     /** Set the password for encrypting the master key.
   2295      *  @param type One of the CRYPTO_TYPE_XXX consts defined in StorageManager.
   2296      *  @param password The password to set.
   2297      */
   2298     public int changeEncryptionPassword(int type, String password) {
   2299         mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
   2300             "no permission to access the crypt keeper");
   2301 
   2302         waitForReady();
   2303 
   2304         if (DEBUG_EVENTS) {
   2305             Slog.i(TAG, "changing encryption password...");
   2306         }
   2307 
   2308         try {
   2309             NativeDaemonEvent event = mConnector.execute("cryptfs", "changepw", CRYPTO_TYPES[type],
   2310                         new SensitiveArg(toHex(password)));
   2311             return Integer.parseInt(event.getMessage());
   2312         } catch (NativeDaemonConnectorException e) {
   2313             // Encryption failed
   2314             return e.getCode();
   2315         }
   2316     }
   2317 
   2318     /**
   2319      * Validate a user-supplied password string with cryptfs
   2320      */
   2321     @Override
   2322     public int verifyEncryptionPassword(String password) throws RemoteException {
   2323         // Only the system process is permitted to validate passwords
   2324         if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
   2325             throw new SecurityException("no permission to access the crypt keeper");
   2326         }
   2327 
   2328         mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
   2329             "no permission to access the crypt keeper");
   2330 
   2331         if (TextUtils.isEmpty(password)) {
   2332             throw new IllegalArgumentException("password cannot be empty");
   2333         }
   2334 
   2335         waitForReady();
   2336 
   2337         if (DEBUG_EVENTS) {
   2338             Slog.i(TAG, "validating encryption password...");
   2339         }
   2340 
   2341         final NativeDaemonEvent event;
   2342         try {
   2343             event = mConnector.execute("cryptfs", "verifypw", new SensitiveArg(toHex(password)));
   2344             Slog.i(TAG, "cryptfs verifypw => " + event.getMessage());
   2345             return Integer.parseInt(event.getMessage());
   2346         } catch (NativeDaemonConnectorException e) {
   2347             // Encryption failed
   2348             return e.getCode();
   2349         }
   2350     }
   2351 
   2352     /**
   2353      * Get the type of encryption used to encrypt the master key.
   2354      * @return The type, one of the CRYPT_TYPE_XXX consts from StorageManager.
   2355      */
   2356     @Override
   2357     public int getPasswordType() {
   2358 
   2359         waitForReady();
   2360 
   2361         final NativeDaemonEvent event;
   2362         try {
   2363             event = mConnector.execute("cryptfs", "getpwtype");
   2364             for (int i = 0; i < CRYPTO_TYPES.length; ++i) {
   2365                 if (CRYPTO_TYPES[i].equals(event.getMessage()))
   2366                     return i;
   2367             }
   2368 
   2369             throw new IllegalStateException("unexpected return from cryptfs");
   2370         } catch (NativeDaemonConnectorException e) {
   2371             throw e.rethrowAsParcelableException();
   2372         }
   2373     }
   2374 
   2375     /**
   2376      * Set a field in the crypto header.
   2377      * @param field field to set
   2378      * @param contents contents to set in field
   2379      */
   2380     @Override
   2381     public void setField(String field, String contents) throws RemoteException {
   2382 
   2383         waitForReady();
   2384 
   2385         final NativeDaemonEvent event;
   2386         try {
   2387             event = mConnector.execute("cryptfs", "setfield", field, contents);
   2388         } catch (NativeDaemonConnectorException e) {
   2389             throw e.rethrowAsParcelableException();
   2390         }
   2391     }
   2392 
   2393     /**
   2394      * Gets a field from the crypto header.
   2395      * @param field field to get
   2396      * @return contents of field
   2397      */
   2398     @Override
   2399     public String getField(String field) throws RemoteException {
   2400 
   2401         waitForReady();
   2402 
   2403         final NativeDaemonEvent event;
   2404         try {
   2405             final String[] contents = NativeDaemonEvent.filterMessageList(
   2406                     mConnector.executeForList("cryptfs", "getfield", field),
   2407                     VoldResponseCode.CryptfsGetfieldResult);
   2408             String result = new String();
   2409             for (String content : contents) {
   2410                 result += content;
   2411             }
   2412             return result;
   2413         } catch (NativeDaemonConnectorException e) {
   2414             throw e.rethrowAsParcelableException();
   2415         }
   2416     }
   2417 
   2418     @Override
   2419     public String getPassword() throws RemoteException {
   2420         if (!isReady()) {
   2421             return new String();
   2422         }
   2423 
   2424         final NativeDaemonEvent event;
   2425         try {
   2426             event = mConnector.execute("cryptfs", "getpw");
   2427             return fromHex(event.getMessage());
   2428         } catch (NativeDaemonConnectorException e) {
   2429             throw e.rethrowAsParcelableException();
   2430         }
   2431     }
   2432 
   2433     @Override
   2434     public void clearPassword() throws RemoteException {
   2435         if (!isReady()) {
   2436             return;
   2437         }
   2438 
   2439         final NativeDaemonEvent event;
   2440         try {
   2441             event = mConnector.execute("cryptfs", "clearpw");
   2442         } catch (NativeDaemonConnectorException e) {
   2443             throw e.rethrowAsParcelableException();
   2444         }
   2445     }
   2446 
   2447     @Override
   2448     public int mkdirs(String callingPkg, String appPath) {
   2449         final int userId = UserHandle.getUserId(Binder.getCallingUid());
   2450         final UserEnvironment userEnv = new UserEnvironment(userId);
   2451 
   2452         // Validate that reported package name belongs to caller
   2453         final AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(
   2454                 Context.APP_OPS_SERVICE);
   2455         appOps.checkPackage(Binder.getCallingUid(), callingPkg);
   2456 
   2457         try {
   2458             appPath = new File(appPath).getCanonicalPath();
   2459         } catch (IOException e) {
   2460             Slog.e(TAG, "Failed to resolve " + appPath + ": " + e);
   2461             return -1;
   2462         }
   2463 
   2464         if (!appPath.endsWith("/")) {
   2465             appPath = appPath + "/";
   2466         }
   2467 
   2468         // Try translating the app path into a vold path, but require that it
   2469         // belong to the calling package.
   2470         String voldPath = maybeTranslatePathForVold(appPath,
   2471                 userEnv.buildExternalStorageAppDataDirs(callingPkg),
   2472                 userEnv.buildExternalStorageAppDataDirsForVold(callingPkg));
   2473         if (voldPath != null) {
   2474             try {
   2475                 mConnector.execute("volume", "mkdirs", voldPath);
   2476                 return 0;
   2477             } catch (NativeDaemonConnectorException e) {
   2478                 return e.getCode();
   2479             }
   2480         }
   2481 
   2482         voldPath = maybeTranslatePathForVold(appPath,
   2483                 userEnv.buildExternalStorageAppObbDirs(callingPkg),
   2484                 userEnv.buildExternalStorageAppObbDirsForVold(callingPkg));
   2485         if (voldPath != null) {
   2486             try {
   2487                 mConnector.execute("volume", "mkdirs", voldPath);
   2488                 return 0;
   2489             } catch (NativeDaemonConnectorException e) {
   2490                 return e.getCode();
   2491             }
   2492         }
   2493 
   2494         voldPath = maybeTranslatePathForVold(appPath,
   2495                 userEnv.buildExternalStorageAppMediaDirs(callingPkg),
   2496                 userEnv.buildExternalStorageAppMediaDirsForVold(callingPkg));
   2497         if (voldPath != null) {
   2498             try {
   2499                 mConnector.execute("volume", "mkdirs", voldPath);
   2500                 return 0;
   2501             } catch (NativeDaemonConnectorException e) {
   2502                 return e.getCode();
   2503             }
   2504         }
   2505 
   2506         throw new SecurityException("Invalid mkdirs path: " + appPath);
   2507     }
   2508 
   2509     /**
   2510      * Translate the given path from an app-visible path to a vold-visible path,
   2511      * but only if it's under the given whitelisted paths.
   2512      *
   2513      * @param path a canonicalized app-visible path.
   2514      * @param appPaths list of app-visible paths that are allowed.
   2515      * @param voldPaths list of vold-visible paths directly corresponding to the
   2516      *            allowed app-visible paths argument.
   2517      * @return a vold-visible path representing the original path, or
   2518      *         {@code null} if the given path didn't have an app-to-vold
   2519      *         mapping.
   2520      */
   2521     @VisibleForTesting
   2522     public static String maybeTranslatePathForVold(
   2523             String path, File[] appPaths, File[] voldPaths) {
   2524         if (appPaths.length != voldPaths.length) {
   2525             throw new IllegalStateException("Paths must be 1:1 mapping");
   2526         }
   2527 
   2528         for (int i = 0; i < appPaths.length; i++) {
   2529             final String appPath = appPaths[i].getAbsolutePath() + "/";
   2530             if (path.startsWith(appPath)) {
   2531                 path = new File(voldPaths[i], path.substring(appPath.length()))
   2532                         .getAbsolutePath();
   2533                 if (!path.endsWith("/")) {
   2534                     path = path + "/";
   2535                 }
   2536                 return path;
   2537             }
   2538         }
   2539         return null;
   2540     }
   2541 
   2542     @Override
   2543     public StorageVolume[] getVolumeList() {
   2544         final int callingUserId = UserHandle.getCallingUserId();
   2545         final boolean accessAll = (mContext.checkPermission(
   2546                 android.Manifest.permission.ACCESS_ALL_EXTERNAL_STORAGE,
   2547                 Binder.getCallingPid(), Binder.getCallingUid()) == PERMISSION_GRANTED);
   2548 
   2549         synchronized (mVolumesLock) {
   2550             final ArrayList<StorageVolume> filtered = Lists.newArrayList();
   2551             for (StorageVolume volume : mVolumes) {
   2552                 final UserHandle owner = volume.getOwner();
   2553                 final boolean ownerMatch = owner == null || owner.getIdentifier() == callingUserId;
   2554                 if (accessAll || ownerMatch) {
   2555                     filtered.add(volume);
   2556                 }
   2557             }
   2558             return filtered.toArray(new StorageVolume[filtered.size()]);
   2559         }
   2560     }
   2561 
   2562     private void addObbStateLocked(ObbState obbState) throws RemoteException {
   2563         final IBinder binder = obbState.getBinder();
   2564         List<ObbState> obbStates = mObbMounts.get(binder);
   2565 
   2566         if (obbStates == null) {
   2567             obbStates = new ArrayList<ObbState>();
   2568             mObbMounts.put(binder, obbStates);
   2569         } else {
   2570             for (final ObbState o : obbStates) {
   2571                 if (o.rawPath.equals(obbState.rawPath)) {
   2572                     throw new IllegalStateException("Attempt to add ObbState twice. "
   2573                             + "This indicates an error in the MountService logic.");
   2574                 }
   2575             }
   2576         }
   2577 
   2578         obbStates.add(obbState);
   2579         try {
   2580             obbState.link();
   2581         } catch (RemoteException e) {
   2582             /*
   2583              * The binder died before we could link it, so clean up our state
   2584              * and return failure.
   2585              */
   2586             obbStates.remove(obbState);
   2587             if (obbStates.isEmpty()) {
   2588                 mObbMounts.remove(binder);
   2589             }
   2590 
   2591             // Rethrow the error so mountObb can get it
   2592             throw e;
   2593         }
   2594 
   2595         mObbPathToStateMap.put(obbState.rawPath, obbState);
   2596     }
   2597 
   2598     private void removeObbStateLocked(ObbState obbState) {
   2599         final IBinder binder = obbState.getBinder();
   2600         final List<ObbState> obbStates = mObbMounts.get(binder);
   2601         if (obbStates != null) {
   2602             if (obbStates.remove(obbState)) {
   2603                 obbState.unlink();
   2604             }
   2605             if (obbStates.isEmpty()) {
   2606                 mObbMounts.remove(binder);
   2607             }
   2608         }
   2609 
   2610         mObbPathToStateMap.remove(obbState.rawPath);
   2611     }
   2612 
   2613     private class ObbActionHandler extends Handler {
   2614         private boolean mBound = false;
   2615         private final List<ObbAction> mActions = new LinkedList<ObbAction>();
   2616 
   2617         ObbActionHandler(Looper l) {
   2618             super(l);
   2619         }
   2620 
   2621         @Override
   2622         public void handleMessage(Message msg) {
   2623             switch (msg.what) {
   2624                 case OBB_RUN_ACTION: {
   2625                     final ObbAction action = (ObbAction) msg.obj;
   2626 
   2627                     if (DEBUG_OBB)
   2628                         Slog.i(TAG, "OBB_RUN_ACTION: " + action.toString());
   2629 
   2630                     // If a bind was already initiated we don't really
   2631                     // need to do anything. The pending install
   2632                     // will be processed later on.
   2633                     if (!mBound) {
   2634                         // If this is the only one pending we might
   2635                         // have to bind to the service again.
   2636                         if (!connectToService()) {
   2637                             Slog.e(TAG, "Failed to bind to media container service");
   2638                             action.handleError();
   2639                             return;
   2640                         }
   2641                     }
   2642 
   2643                     mActions.add(action);
   2644                     break;
   2645                 }
   2646                 case OBB_MCS_BOUND: {
   2647                     if (DEBUG_OBB)
   2648                         Slog.i(TAG, "OBB_MCS_BOUND");
   2649                     if (msg.obj != null) {
   2650                         mContainerService = (IMediaContainerService) msg.obj;
   2651                     }
   2652                     if (mContainerService == null) {
   2653                         // Something seriously wrong. Bail out
   2654                         Slog.e(TAG, "Cannot bind to media container service");
   2655                         for (ObbAction action : mActions) {
   2656                             // Indicate service bind error
   2657                             action.handleError();
   2658                         }
   2659                         mActions.clear();
   2660                     } else if (mActions.size() > 0) {
   2661                         final ObbAction action = mActions.get(0);
   2662                         if (action != null) {
   2663                             action.execute(this);
   2664                         }
   2665                     } else {
   2666                         // Should never happen ideally.
   2667                         Slog.w(TAG, "Empty queue");
   2668                     }
   2669                     break;
   2670                 }
   2671                 case OBB_MCS_RECONNECT: {
   2672                     if (DEBUG_OBB)
   2673                         Slog.i(TAG, "OBB_MCS_RECONNECT");
   2674                     if (mActions.size() > 0) {
   2675                         if (mBound) {
   2676                             disconnectService();
   2677                         }
   2678                         if (!connectToService()) {
   2679                             Slog.e(TAG, "Failed to bind to media container service");
   2680                             for (ObbAction action : mActions) {
   2681                                 // Indicate service bind error
   2682                                 action.handleError();
   2683                             }
   2684                             mActions.clear();
   2685                         }
   2686                     }
   2687                     break;
   2688                 }
   2689                 case OBB_MCS_UNBIND: {
   2690                     if (DEBUG_OBB)
   2691                         Slog.i(TAG, "OBB_MCS_UNBIND");
   2692 
   2693                     // Delete pending install
   2694                     if (mActions.size() > 0) {
   2695                         mActions.remove(0);
   2696                     }
   2697                     if (mActions.size() == 0) {
   2698                         if (mBound) {
   2699                             disconnectService();
   2700                         }
   2701                     } else {
   2702                         // There are more pending requests in queue.
   2703                         // Just post MCS_BOUND message to trigger processing
   2704                         // of next pending install.
   2705                         mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND);
   2706                     }
   2707                     break;
   2708                 }
   2709                 case OBB_FLUSH_MOUNT_STATE: {
   2710                     final String path = (String) msg.obj;
   2711 
   2712                     if (DEBUG_OBB)
   2713                         Slog.i(TAG, "Flushing all OBB state for path " + path);
   2714 
   2715                     synchronized (mObbMounts) {
   2716                         final List<ObbState> obbStatesToRemove = new LinkedList<ObbState>();
   2717 
   2718                         final Iterator<ObbState> i = mObbPathToStateMap.values().iterator();
   2719                         while (i.hasNext()) {
   2720                             final ObbState state = i.next();
   2721 
   2722                             /*
   2723                              * If this entry's source file is in the volume path
   2724                              * that got unmounted, remove it because it's no
   2725                              * longer valid.
   2726                              */
   2727                             if (state.canonicalPath.startsWith(path)) {
   2728                                 obbStatesToRemove.add(state);
   2729                             }
   2730                         }
   2731 
   2732                         for (final ObbState obbState : obbStatesToRemove) {
   2733                             if (DEBUG_OBB)
   2734                                 Slog.i(TAG, "Removing state for " + obbState.rawPath);
   2735 
   2736                             removeObbStateLocked(obbState);
   2737 
   2738                             try {
   2739                                 obbState.token.onObbResult(obbState.rawPath, obbState.nonce,
   2740                                         OnObbStateChangeListener.UNMOUNTED);
   2741                             } catch (RemoteException e) {
   2742                                 Slog.i(TAG, "Couldn't send unmount notification for  OBB: "
   2743                                         + obbState.rawPath);
   2744                             }
   2745                         }
   2746                     }
   2747                     break;
   2748                 }
   2749             }
   2750         }
   2751 
   2752         private boolean connectToService() {
   2753             if (DEBUG_OBB)
   2754                 Slog.i(TAG, "Trying to bind to DefaultContainerService");
   2755 
   2756             Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
   2757             if (mContext.bindService(service, mDefContainerConn, Context.BIND_AUTO_CREATE)) {
   2758                 mBound = true;
   2759                 return true;
   2760             }
   2761             return false;
   2762         }
   2763 
   2764         private void disconnectService() {
   2765             mContainerService = null;
   2766             mBound = false;
   2767             mContext.unbindService(mDefContainerConn);
   2768         }
   2769     }
   2770 
   2771     abstract class ObbAction {
   2772         private static final int MAX_RETRIES = 3;
   2773         private int mRetries;
   2774 
   2775         ObbState mObbState;
   2776 
   2777         ObbAction(ObbState obbState) {
   2778             mObbState = obbState;
   2779         }
   2780 
   2781         public void execute(ObbActionHandler handler) {
   2782             try {
   2783                 if (DEBUG_OBB)
   2784                     Slog.i(TAG, "Starting to execute action: " + toString());
   2785                 mRetries++;
   2786                 if (mRetries > MAX_RETRIES) {
   2787                     Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
   2788                     mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
   2789                     handleError();
   2790                     return;
   2791                 } else {
   2792                     handleExecute();
   2793                     if (DEBUG_OBB)
   2794                         Slog.i(TAG, "Posting install MCS_UNBIND");
   2795                     mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
   2796                 }
   2797             } catch (RemoteException e) {
   2798                 if (DEBUG_OBB)
   2799                     Slog.i(TAG, "Posting install MCS_RECONNECT");
   2800                 mObbActionHandler.sendEmptyMessage(OBB_MCS_RECONNECT);
   2801             } catch (Exception e) {
   2802                 if (DEBUG_OBB)
   2803                     Slog.d(TAG, "Error handling OBB action", e);
   2804                 handleError();
   2805                 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
   2806             }
   2807         }
   2808 
   2809         abstract void handleExecute() throws RemoteException, IOException;
   2810         abstract void handleError();
   2811 
   2812         protected ObbInfo getObbInfo() throws IOException {
   2813             ObbInfo obbInfo;
   2814             try {
   2815                 obbInfo = mContainerService.getObbInfo(mObbState.ownerPath);
   2816             } catch (RemoteException e) {
   2817                 Slog.d(TAG, "Couldn't call DefaultContainerService to fetch OBB info for "
   2818                         + mObbState.ownerPath);
   2819                 obbInfo = null;
   2820             }
   2821             if (obbInfo == null) {
   2822                 throw new IOException("Couldn't read OBB file: " + mObbState.ownerPath);
   2823             }
   2824             return obbInfo;
   2825         }
   2826 
   2827         protected void sendNewStatusOrIgnore(int status) {
   2828             if (mObbState == null || mObbState.token == null) {
   2829                 return;
   2830             }
   2831 
   2832             try {
   2833                 mObbState.token.onObbResult(mObbState.rawPath, mObbState.nonce, status);
   2834             } catch (RemoteException e) {
   2835                 Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
   2836             }
   2837         }
   2838     }
   2839 
   2840     class MountObbAction extends ObbAction {
   2841         private final String mKey;
   2842         private final int mCallingUid;
   2843 
   2844         MountObbAction(ObbState obbState, String key, int callingUid) {
   2845             super(obbState);
   2846             mKey = key;
   2847             mCallingUid = callingUid;
   2848         }
   2849 
   2850         @Override
   2851         public void handleExecute() throws IOException, RemoteException {
   2852             waitForReady();
   2853             warnOnNotMounted();
   2854 
   2855             final ObbInfo obbInfo = getObbInfo();
   2856 
   2857             if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mCallingUid)) {
   2858                 Slog.w(TAG, "Denied attempt to mount OBB " + obbInfo.filename
   2859                         + " which is owned by " + obbInfo.packageName);
   2860                 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
   2861                 return;
   2862             }
   2863 
   2864             final boolean isMounted;
   2865             synchronized (mObbMounts) {
   2866                 isMounted = mObbPathToStateMap.containsKey(mObbState.rawPath);
   2867             }
   2868             if (isMounted) {
   2869                 Slog.w(TAG, "Attempt to mount OBB which is already mounted: " + obbInfo.filename);
   2870                 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_ALREADY_MOUNTED);
   2871                 return;
   2872             }
   2873 
   2874             final String hashedKey;
   2875             if (mKey == null) {
   2876                 hashedKey = "none";
   2877             } else {
   2878                 try {
   2879                     SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
   2880 
   2881                     KeySpec ks = new PBEKeySpec(mKey.toCharArray(), obbInfo.salt,
   2882                             PBKDF2_HASH_ROUNDS, CRYPTO_ALGORITHM_KEY_SIZE);
   2883                     SecretKey key = factory.generateSecret(ks);
   2884                     BigInteger bi = new BigInteger(key.getEncoded());
   2885                     hashedKey = bi.toString(16);
   2886                 } catch (NoSuchAlgorithmException e) {
   2887                     Slog.e(TAG, "Could not load PBKDF2 algorithm", e);
   2888                     sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
   2889                     return;
   2890                 } catch (InvalidKeySpecException e) {
   2891                     Slog.e(TAG, "Invalid key spec when loading PBKDF2 algorithm", e);
   2892                     sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
   2893                     return;
   2894                 }
   2895             }
   2896 
   2897             int rc = StorageResultCode.OperationSucceeded;
   2898             try {
   2899                 mConnector.execute("obb", "mount", mObbState.voldPath, new SensitiveArg(hashedKey),
   2900                         mObbState.ownerGid);
   2901             } catch (NativeDaemonConnectorException e) {
   2902                 int code = e.getCode();
   2903                 if (code != VoldResponseCode.OpFailedStorageBusy) {
   2904                     rc = StorageResultCode.OperationFailedInternalError;
   2905                 }
   2906             }
   2907 
   2908             if (rc == StorageResultCode.OperationSucceeded) {
   2909                 if (DEBUG_OBB)
   2910                     Slog.d(TAG, "Successfully mounted OBB " + mObbState.voldPath);
   2911 
   2912                 synchronized (mObbMounts) {
   2913                     addObbStateLocked(mObbState);
   2914                 }
   2915 
   2916                 sendNewStatusOrIgnore(OnObbStateChangeListener.MOUNTED);
   2917             } else {
   2918                 Slog.e(TAG, "Couldn't mount OBB file: " + rc);
   2919 
   2920                 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT);
   2921             }
   2922         }
   2923 
   2924         @Override
   2925         public void handleError() {
   2926             sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
   2927         }
   2928 
   2929         @Override
   2930         public String toString() {
   2931             StringBuilder sb = new StringBuilder();
   2932             sb.append("MountObbAction{");
   2933             sb.append(mObbState);
   2934             sb.append('}');
   2935             return sb.toString();
   2936         }
   2937     }
   2938 
   2939     class UnmountObbAction extends ObbAction {
   2940         private final boolean mForceUnmount;
   2941 
   2942         UnmountObbAction(ObbState obbState, boolean force) {
   2943             super(obbState);
   2944             mForceUnmount = force;
   2945         }
   2946 
   2947         @Override
   2948         public void handleExecute() throws IOException {
   2949             waitForReady();
   2950             warnOnNotMounted();
   2951 
   2952             final ObbInfo obbInfo = getObbInfo();
   2953 
   2954             final ObbState existingState;
   2955             synchronized (mObbMounts) {
   2956                 existingState = mObbPathToStateMap.get(mObbState.rawPath);
   2957             }
   2958 
   2959             if (existingState == null) {
   2960                 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_NOT_MOUNTED);
   2961                 return;
   2962             }
   2963 
   2964             if (existingState.ownerGid != mObbState.ownerGid) {
   2965                 Slog.w(TAG, "Permission denied attempting to unmount OBB " + existingState.rawPath
   2966                         + " (owned by GID " + existingState.ownerGid + ")");
   2967                 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
   2968                 return;
   2969             }
   2970 
   2971             int rc = StorageResultCode.OperationSucceeded;
   2972             try {
   2973                 final Command cmd = new Command("obb", "unmount", mObbState.voldPath);
   2974                 if (mForceUnmount) {
   2975                     cmd.appendArg("force");
   2976                 }
   2977                 mConnector.execute(cmd);
   2978             } catch (NativeDaemonConnectorException e) {
   2979                 int code = e.getCode();
   2980                 if (code == VoldResponseCode.OpFailedStorageBusy) {
   2981                     rc = StorageResultCode.OperationFailedStorageBusy;
   2982                 } else if (code == VoldResponseCode.OpFailedStorageNotFound) {
   2983                     // If it's not mounted then we've already won.
   2984                     rc = StorageResultCode.OperationSucceeded;
   2985                 } else {
   2986                     rc = StorageResultCode.OperationFailedInternalError;
   2987                 }
   2988             }
   2989 
   2990             if (rc == StorageResultCode.OperationSucceeded) {
   2991                 synchronized (mObbMounts) {
   2992                     removeObbStateLocked(existingState);
   2993                 }
   2994 
   2995                 sendNewStatusOrIgnore(OnObbStateChangeListener.UNMOUNTED);
   2996             } else {
   2997                 Slog.w(TAG, "Could not unmount OBB: " + existingState);
   2998                 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_UNMOUNT);
   2999             }
   3000         }
   3001 
   3002         @Override
   3003         public void handleError() {
   3004             sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
   3005         }
   3006 
   3007         @Override
   3008         public String toString() {
   3009             StringBuilder sb = new StringBuilder();
   3010             sb.append("UnmountObbAction{");
   3011             sb.append(mObbState);
   3012             sb.append(",force=");
   3013             sb.append(mForceUnmount);
   3014             sb.append('}');
   3015             return sb.toString();
   3016         }
   3017     }
   3018 
   3019     @VisibleForTesting
   3020     public static String buildObbPath(final String canonicalPath, int userId, boolean forVold) {
   3021         // TODO: allow caller to provide Environment for full testing
   3022         // TODO: extend to support OBB mounts on secondary external storage
   3023 
   3024         // Only adjust paths when storage is emulated
   3025         if (!Environment.isExternalStorageEmulated()) {
   3026             return canonicalPath;
   3027         }
   3028 
   3029         String path = canonicalPath.toString();
   3030 
   3031         // First trim off any external storage prefix
   3032         final UserEnvironment userEnv = new UserEnvironment(userId);
   3033 
   3034         // /storage/emulated/0
   3035         final String externalPath = userEnv.getExternalStorageDirectory().getAbsolutePath();
   3036         // /storage/emulated_legacy
   3037         final String legacyExternalPath = Environment.getLegacyExternalStorageDirectory()
   3038                 .getAbsolutePath();
   3039 
   3040         if (path.startsWith(externalPath)) {
   3041             path = path.substring(externalPath.length() + 1);
   3042         } else if (path.startsWith(legacyExternalPath)) {
   3043             path = path.substring(legacyExternalPath.length() + 1);
   3044         } else {
   3045             return canonicalPath;
   3046         }
   3047 
   3048         // Handle special OBB paths on emulated storage
   3049         final String obbPath = "Android/obb";
   3050         if (path.startsWith(obbPath)) {
   3051             path = path.substring(obbPath.length() + 1);
   3052 
   3053             if (forVold) {
   3054                 return new File(Environment.getEmulatedStorageObbSource(), path).getAbsolutePath();
   3055             } else {
   3056                 final UserEnvironment ownerEnv = new UserEnvironment(UserHandle.USER_OWNER);
   3057                 return new File(ownerEnv.buildExternalStorageAndroidObbDirs()[0], path)
   3058                         .getAbsolutePath();
   3059             }
   3060         }
   3061 
   3062         // Handle normal external storage paths
   3063         if (forVold) {
   3064             return new File(Environment.getEmulatedStorageSource(userId), path).getAbsolutePath();
   3065         } else {
   3066             return new File(userEnv.getExternalDirsForApp()[0], path).getAbsolutePath();
   3067         }
   3068     }
   3069 
   3070     @Override
   3071     protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
   3072         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
   3073 
   3074         final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ", 160);
   3075 
   3076         synchronized (mObbMounts) {
   3077             pw.println("mObbMounts:");
   3078             pw.increaseIndent();
   3079             final Iterator<Entry<IBinder, List<ObbState>>> binders = mObbMounts.entrySet()
   3080                     .iterator();
   3081             while (binders.hasNext()) {
   3082                 Entry<IBinder, List<ObbState>> e = binders.next();
   3083                 pw.println(e.getKey() + ":");
   3084                 pw.increaseIndent();
   3085                 final List<ObbState> obbStates = e.getValue();
   3086                 for (final ObbState obbState : obbStates) {
   3087                     pw.println(obbState);
   3088                 }
   3089                 pw.decreaseIndent();
   3090             }
   3091             pw.decreaseIndent();
   3092 
   3093             pw.println();
   3094             pw.println("mObbPathToStateMap:");
   3095             pw.increaseIndent();
   3096             final Iterator<Entry<String, ObbState>> maps = mObbPathToStateMap.entrySet().iterator();
   3097             while (maps.hasNext()) {
   3098                 final Entry<String, ObbState> e = maps.next();
   3099                 pw.print(e.getKey());
   3100                 pw.print(" -> ");
   3101                 pw.println(e.getValue());
   3102             }
   3103             pw.decreaseIndent();
   3104         }
   3105 
   3106         synchronized (mVolumesLock) {
   3107             pw.println();
   3108             pw.println("mVolumes:");
   3109             pw.increaseIndent();
   3110             for (StorageVolume volume : mVolumes) {
   3111                 pw.println(volume);
   3112                 pw.increaseIndent();
   3113                 pw.println("Current state: " + mVolumeStates.get(volume.getPath()));
   3114                 pw.decreaseIndent();
   3115             }
   3116             pw.decreaseIndent();
   3117         }
   3118 
   3119         pw.println();
   3120         pw.println("mConnection:");
   3121         pw.increaseIndent();
   3122         mConnector.dump(fd, pw, args);
   3123         pw.decreaseIndent();
   3124 
   3125         SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
   3126 
   3127         pw.println();
   3128         pw.print("Last maintenance: ");
   3129         pw.println(sdf.format(new Date(mLastMaintenance)));
   3130     }
   3131 
   3132     /** {@inheritDoc} */
   3133     public void monitor() {
   3134         if (mConnector != null) {
   3135             mConnector.monitor();
   3136         }
   3137     }
   3138 }
   3139