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