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