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