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