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