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