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