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