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