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