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