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