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