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