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