Home | History | Annotate | Download | only in storage
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.os.storage;
     18 
     19 import static android.net.TrafficStats.MB_IN_BYTES;
     20 
     21 import android.annotation.NonNull;
     22 import android.annotation.Nullable;
     23 import android.annotation.SdkConstant;
     24 import android.app.ActivityThread;
     25 import android.content.ContentResolver;
     26 import android.content.Context;
     27 import android.content.pm.IPackageMoveObserver;
     28 import android.content.pm.PackageManager;
     29 import android.os.Binder;
     30 import android.os.Environment;
     31 import android.os.FileUtils;
     32 import android.os.Handler;
     33 import android.os.Looper;
     34 import android.os.Message;
     35 import android.os.ParcelFileDescriptor;
     36 import android.os.RemoteException;
     37 import android.os.ServiceManager;
     38 import android.os.SystemProperties;
     39 import android.os.UserHandle;
     40 import android.provider.Settings;
     41 import android.text.TextUtils;
     42 import android.util.Log;
     43 import android.util.Slog;
     44 import android.util.SparseArray;
     45 
     46 import com.android.internal.os.SomeArgs;
     47 import com.android.internal.util.Preconditions;
     48 
     49 import java.io.BufferedReader;
     50 import java.io.File;
     51 import java.io.FileInputStream;
     52 import java.io.IOException;
     53 import java.io.InputStreamReader;
     54 import java.lang.ref.WeakReference;
     55 import java.util.ArrayList;
     56 import java.util.Arrays;
     57 import java.util.Collections;
     58 import java.util.Iterator;
     59 import java.util.List;
     60 import java.util.Objects;
     61 import java.util.concurrent.atomic.AtomicInteger;
     62 
     63 /**
     64  * StorageManager is the interface to the systems storage service. The storage
     65  * manager handles storage-related items such as Opaque Binary Blobs (OBBs).
     66  * <p>
     67  * OBBs contain a filesystem that maybe be encrypted on disk and mounted
     68  * on-demand from an application. OBBs are a good way of providing large amounts
     69  * of binary assets without packaging them into APKs as they may be multiple
     70  * gigabytes in size. However, due to their size, they're most likely stored in
     71  * a shared storage pool accessible from all programs. The system does not
     72  * guarantee the security of the OBB file itself: if any program modifies the
     73  * OBB, there is no guarantee that a read from that OBB will produce the
     74  * expected output.
     75  * <p>
     76  * Get an instance of this class by calling
     77  * {@link android.content.Context#getSystemService(java.lang.String)} with an
     78  * argument of {@link android.content.Context#STORAGE_SERVICE}.
     79  */
     80 public class StorageManager {
     81     private static final String TAG = "StorageManager";
     82 
     83     /** {@hide} */
     84     public static final String PROP_PRIMARY_PHYSICAL = "ro.vold.primary_physical";
     85     /** {@hide} */
     86     public static final String PROP_HAS_ADOPTABLE = "vold.has_adoptable";
     87     /** {@hide} */
     88     public static final String PROP_FORCE_ADOPTABLE = "persist.fw.force_adoptable";
     89     /** {@hide} */
     90     public static final String PROP_EMULATE_FBE = "persist.sys.emulate_fbe";
     91     /** {@hide} */
     92     public static final String PROP_SDCARDFS = "persist.sys.sdcardfs";
     93 
     94     /** {@hide} */
     95     public static final String UUID_PRIVATE_INTERNAL = null;
     96     /** {@hide} */
     97     public static final String UUID_PRIMARY_PHYSICAL = "primary_physical";
     98 
     99 
    100     /**
    101      * Activity Action: Allows the user to manage their storage. This activity provides the ability
    102      * to free up space on the device by deleting data such as apps.
    103      * <p>
    104      * Input: Nothing.
    105      * <p>
    106      * Output: Nothing.
    107      */
    108     @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
    109     public static final String ACTION_MANAGE_STORAGE
    110             = "android.os.storage.action.MANAGE_STORAGE";
    111 
    112     /** {@hide} */
    113     public static final int DEBUG_FORCE_ADOPTABLE = 1 << 0;
    114     /** {@hide} */
    115     public static final int DEBUG_EMULATE_FBE = 1 << 1;
    116     /** {@hide} */
    117     public static final int DEBUG_SDCARDFS_FORCE_ON = 1 << 2;
    118     /** {@hide} */
    119     public static final int DEBUG_SDCARDFS_FORCE_OFF = 1 << 3;
    120 
    121     // NOTE: keep in sync with installd
    122     /** {@hide} */
    123     public static final int FLAG_STORAGE_DE = 1 << 0;
    124     /** {@hide} */
    125     public static final int FLAG_STORAGE_CE = 1 << 1;
    126 
    127     /** {@hide} */
    128     public static final int FLAG_FOR_WRITE = 1 << 8;
    129     /** {@hide} */
    130     public static final int FLAG_REAL_STATE = 1 << 9;
    131     /** {@hide} */
    132     public static final int FLAG_INCLUDE_INVISIBLE = 1 << 10;
    133 
    134     private static volatile IMountService sMountService = null;
    135 
    136     // TODO: the location of the primary storage block varies from device to device, so we need to
    137     // try the most likely candidates - a long-term solution would be a device-specific vold
    138     // function that returns the calculated size.
    139     private static final String[] INTERNAL_STORAGE_SIZE_PATHS = {
    140             "/sys/block/mmcblk0/size",
    141             "/sys/block/sda/size"
    142     };
    143     private static final int INTERNAL_STORAGE_SECTOR_SIZE = 512;
    144 
    145     private final Context mContext;
    146     private final ContentResolver mResolver;
    147 
    148     private final IMountService mMountService;
    149     private final Looper mLooper;
    150     private final AtomicInteger mNextNonce = new AtomicInteger(0);
    151 
    152     private final ArrayList<StorageEventListenerDelegate> mDelegates = new ArrayList<>();
    153 
    154     private static class StorageEventListenerDelegate extends IMountServiceListener.Stub implements
    155             Handler.Callback {
    156         private static final int MSG_STORAGE_STATE_CHANGED = 1;
    157         private static final int MSG_VOLUME_STATE_CHANGED = 2;
    158         private static final int MSG_VOLUME_RECORD_CHANGED = 3;
    159         private static final int MSG_VOLUME_FORGOTTEN = 4;
    160         private static final int MSG_DISK_SCANNED = 5;
    161         private static final int MSG_DISK_DESTROYED = 6;
    162 
    163         final StorageEventListener mCallback;
    164         final Handler mHandler;
    165 
    166         public StorageEventListenerDelegate(StorageEventListener callback, Looper looper) {
    167             mCallback = callback;
    168             mHandler = new Handler(looper, this);
    169         }
    170 
    171         @Override
    172         public boolean handleMessage(Message msg) {
    173             final SomeArgs args = (SomeArgs) msg.obj;
    174             switch (msg.what) {
    175                 case MSG_STORAGE_STATE_CHANGED:
    176                     mCallback.onStorageStateChanged((String) args.arg1, (String) args.arg2,
    177                             (String) args.arg3);
    178                     args.recycle();
    179                     return true;
    180                 case MSG_VOLUME_STATE_CHANGED:
    181                     mCallback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3);
    182                     args.recycle();
    183                     return true;
    184                 case MSG_VOLUME_RECORD_CHANGED:
    185                     mCallback.onVolumeRecordChanged((VolumeRecord) args.arg1);
    186                     args.recycle();
    187                     return true;
    188                 case MSG_VOLUME_FORGOTTEN:
    189                     mCallback.onVolumeForgotten((String) args.arg1);
    190                     args.recycle();
    191                     return true;
    192                 case MSG_DISK_SCANNED:
    193                     mCallback.onDiskScanned((DiskInfo) args.arg1, args.argi2);
    194                     args.recycle();
    195                     return true;
    196                 case MSG_DISK_DESTROYED:
    197                     mCallback.onDiskDestroyed((DiskInfo) args.arg1);
    198                     args.recycle();
    199                     return true;
    200             }
    201             args.recycle();
    202             return false;
    203         }
    204 
    205         @Override
    206         public void onUsbMassStorageConnectionChanged(boolean connected) throws RemoteException {
    207             // Ignored
    208         }
    209 
    210         @Override
    211         public void onStorageStateChanged(String path, String oldState, String newState) {
    212             final SomeArgs args = SomeArgs.obtain();
    213             args.arg1 = path;
    214             args.arg2 = oldState;
    215             args.arg3 = newState;
    216             mHandler.obtainMessage(MSG_STORAGE_STATE_CHANGED, args).sendToTarget();
    217         }
    218 
    219         @Override
    220         public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
    221             final SomeArgs args = SomeArgs.obtain();
    222             args.arg1 = vol;
    223             args.argi2 = oldState;
    224             args.argi3 = newState;
    225             mHandler.obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget();
    226         }
    227 
    228         @Override
    229         public void onVolumeRecordChanged(VolumeRecord rec) {
    230             final SomeArgs args = SomeArgs.obtain();
    231             args.arg1 = rec;
    232             mHandler.obtainMessage(MSG_VOLUME_RECORD_CHANGED, args).sendToTarget();
    233         }
    234 
    235         @Override
    236         public void onVolumeForgotten(String fsUuid) {
    237             final SomeArgs args = SomeArgs.obtain();
    238             args.arg1 = fsUuid;
    239             mHandler.obtainMessage(MSG_VOLUME_FORGOTTEN, args).sendToTarget();
    240         }
    241 
    242         @Override
    243         public void onDiskScanned(DiskInfo disk, int volumeCount) {
    244             final SomeArgs args = SomeArgs.obtain();
    245             args.arg1 = disk;
    246             args.argi2 = volumeCount;
    247             mHandler.obtainMessage(MSG_DISK_SCANNED, args).sendToTarget();
    248         }
    249 
    250         @Override
    251         public void onDiskDestroyed(DiskInfo disk) throws RemoteException {
    252             final SomeArgs args = SomeArgs.obtain();
    253             args.arg1 = disk;
    254             mHandler.obtainMessage(MSG_DISK_DESTROYED, args).sendToTarget();
    255         }
    256     }
    257 
    258     /**
    259      * Binder listener for OBB action results.
    260      */
    261     private final ObbActionListener mObbActionListener = new ObbActionListener();
    262 
    263     private class ObbActionListener extends IObbActionListener.Stub {
    264         @SuppressWarnings("hiding")
    265         private SparseArray<ObbListenerDelegate> mListeners = new SparseArray<ObbListenerDelegate>();
    266 
    267         @Override
    268         public void onObbResult(String filename, int nonce, int status) {
    269             final ObbListenerDelegate delegate;
    270             synchronized (mListeners) {
    271                 delegate = mListeners.get(nonce);
    272                 if (delegate != null) {
    273                     mListeners.remove(nonce);
    274                 }
    275             }
    276 
    277             if (delegate != null) {
    278                 delegate.sendObbStateChanged(filename, status);
    279             }
    280         }
    281 
    282         public int addListener(OnObbStateChangeListener listener) {
    283             final ObbListenerDelegate delegate = new ObbListenerDelegate(listener);
    284 
    285             synchronized (mListeners) {
    286                 mListeners.put(delegate.nonce, delegate);
    287             }
    288 
    289             return delegate.nonce;
    290         }
    291     }
    292 
    293     private int getNextNonce() {
    294         return mNextNonce.getAndIncrement();
    295     }
    296 
    297     /**
    298      * Private class containing sender and receiver code for StorageEvents.
    299      */
    300     private class ObbListenerDelegate {
    301         private final WeakReference<OnObbStateChangeListener> mObbEventListenerRef;
    302         private final Handler mHandler;
    303 
    304         private final int nonce;
    305 
    306         ObbListenerDelegate(OnObbStateChangeListener listener) {
    307             nonce = getNextNonce();
    308             mObbEventListenerRef = new WeakReference<OnObbStateChangeListener>(listener);
    309             mHandler = new Handler(mLooper) {
    310                 @Override
    311                 public void handleMessage(Message msg) {
    312                     final OnObbStateChangeListener changeListener = getListener();
    313                     if (changeListener == null) {
    314                         return;
    315                     }
    316 
    317                     changeListener.onObbStateChange((String) msg.obj, msg.arg1);
    318                 }
    319             };
    320         }
    321 
    322         OnObbStateChangeListener getListener() {
    323             if (mObbEventListenerRef == null) {
    324                 return null;
    325             }
    326             return mObbEventListenerRef.get();
    327         }
    328 
    329         void sendObbStateChanged(String path, int state) {
    330             mHandler.obtainMessage(0, state, 0, path).sendToTarget();
    331         }
    332     }
    333 
    334     /** {@hide} */
    335     @Deprecated
    336     public static StorageManager from(Context context) {
    337         return context.getSystemService(StorageManager.class);
    338     }
    339 
    340     /**
    341      * Constructs a StorageManager object through which an application can
    342      * can communicate with the systems mount service.
    343      *
    344      * @param tgtLooper The {@link android.os.Looper} which events will be received on.
    345      *
    346      * <p>Applications can get instance of this class by calling
    347      * {@link android.content.Context#getSystemService(java.lang.String)} with an argument
    348      * of {@link android.content.Context#STORAGE_SERVICE}.
    349      *
    350      * @hide
    351      */
    352     public StorageManager(Context context, Looper looper) {
    353         mContext = context;
    354         mResolver = context.getContentResolver();
    355         mLooper = looper;
    356         mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
    357         if (mMountService == null) {
    358             throw new IllegalStateException("Failed to find running mount service");
    359         }
    360     }
    361 
    362     /**
    363      * Registers a {@link android.os.storage.StorageEventListener StorageEventListener}.
    364      *
    365      * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object.
    366      *
    367      * @hide
    368      */
    369     public void registerListener(StorageEventListener listener) {
    370         synchronized (mDelegates) {
    371             final StorageEventListenerDelegate delegate = new StorageEventListenerDelegate(listener,
    372                     mLooper);
    373             try {
    374                 mMountService.registerListener(delegate);
    375             } catch (RemoteException e) {
    376                 throw e.rethrowFromSystemServer();
    377             }
    378             mDelegates.add(delegate);
    379         }
    380     }
    381 
    382     /**
    383      * Unregisters a {@link android.os.storage.StorageEventListener StorageEventListener}.
    384      *
    385      * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object.
    386      *
    387      * @hide
    388      */
    389     public void unregisterListener(StorageEventListener listener) {
    390         synchronized (mDelegates) {
    391             for (Iterator<StorageEventListenerDelegate> i = mDelegates.iterator(); i.hasNext();) {
    392                 final StorageEventListenerDelegate delegate = i.next();
    393                 if (delegate.mCallback == listener) {
    394                     try {
    395                         mMountService.unregisterListener(delegate);
    396                     } catch (RemoteException e) {
    397                         throw e.rethrowFromSystemServer();
    398                     }
    399                     i.remove();
    400                 }
    401             }
    402         }
    403     }
    404 
    405     /**
    406      * Enables USB Mass Storage (UMS) on the device.
    407      *
    408      * @hide
    409      */
    410     @Deprecated
    411     public void enableUsbMassStorage() {
    412     }
    413 
    414     /**
    415      * Disables USB Mass Storage (UMS) on the device.
    416      *
    417      * @hide
    418      */
    419     @Deprecated
    420     public void disableUsbMassStorage() {
    421     }
    422 
    423     /**
    424      * Query if a USB Mass Storage (UMS) host is connected.
    425      * @return true if UMS host is connected.
    426      *
    427      * @hide
    428      */
    429     @Deprecated
    430     public boolean isUsbMassStorageConnected() {
    431         return false;
    432     }
    433 
    434     /**
    435      * Query if a USB Mass Storage (UMS) is enabled on the device.
    436      * @return true if UMS host is enabled.
    437      *
    438      * @hide
    439      */
    440     @Deprecated
    441     public boolean isUsbMassStorageEnabled() {
    442         return false;
    443     }
    444 
    445     /**
    446      * Mount an Opaque Binary Blob (OBB) file. If a <code>key</code> is
    447      * specified, it is supplied to the mounting process to be used in any
    448      * encryption used in the OBB.
    449      * <p>
    450      * The OBB will remain mounted for as long as the StorageManager reference
    451      * is held by the application. As soon as this reference is lost, the OBBs
    452      * in use will be unmounted. The {@link OnObbStateChangeListener} registered
    453      * with this call will receive the success or failure of this operation.
    454      * <p>
    455      * <em>Note:</em> you can only mount OBB files for which the OBB tag on the
    456      * file matches a package ID that is owned by the calling program's UID.
    457      * That is, shared UID applications can attempt to mount any other
    458      * application's OBB that shares its UID.
    459      *
    460      * @param rawPath the path to the OBB file
    461      * @param key secret used to encrypt the OBB; may be <code>null</code> if no
    462      *            encryption was used on the OBB.
    463      * @param listener will receive the success or failure of the operation
    464      * @return whether the mount call was successfully queued or not
    465      */
    466     public boolean mountObb(String rawPath, String key, OnObbStateChangeListener listener) {
    467         Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
    468         Preconditions.checkNotNull(listener, "listener cannot be null");
    469 
    470         try {
    471             final String canonicalPath = new File(rawPath).getCanonicalPath();
    472             final int nonce = mObbActionListener.addListener(listener);
    473             mMountService.mountObb(rawPath, canonicalPath, key, mObbActionListener, nonce);
    474             return true;
    475         } catch (IOException e) {
    476             throw new IllegalArgumentException("Failed to resolve path: " + rawPath, e);
    477         } catch (RemoteException e) {
    478             throw e.rethrowFromSystemServer();
    479         }
    480     }
    481 
    482     /**
    483      * Unmount an Opaque Binary Blob (OBB) file asynchronously. If the
    484      * <code>force</code> flag is true, it will kill any application needed to
    485      * unmount the given OBB (even the calling application).
    486      * <p>
    487      * The {@link OnObbStateChangeListener} registered with this call will
    488      * receive the success or failure of this operation.
    489      * <p>
    490      * <em>Note:</em> you can only mount OBB files for which the OBB tag on the
    491      * file matches a package ID that is owned by the calling program's UID.
    492      * That is, shared UID applications can obtain access to any other
    493      * application's OBB that shares its UID.
    494      * <p>
    495      *
    496      * @param rawPath path to the OBB file
    497      * @param force whether to kill any programs using this in order to unmount
    498      *            it
    499      * @param listener will receive the success or failure of the operation
    500      * @return whether the unmount call was successfully queued or not
    501      */
    502     public boolean unmountObb(String rawPath, boolean force, OnObbStateChangeListener listener) {
    503         Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
    504         Preconditions.checkNotNull(listener, "listener cannot be null");
    505 
    506         try {
    507             final int nonce = mObbActionListener.addListener(listener);
    508             mMountService.unmountObb(rawPath, force, mObbActionListener, nonce);
    509             return true;
    510         } catch (RemoteException e) {
    511             throw e.rethrowFromSystemServer();
    512         }
    513     }
    514 
    515     /**
    516      * Check whether an Opaque Binary Blob (OBB) is mounted or not.
    517      *
    518      * @param rawPath path to OBB image
    519      * @return true if OBB is mounted; false if not mounted or on error
    520      */
    521     public boolean isObbMounted(String rawPath) {
    522         Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
    523 
    524         try {
    525             return mMountService.isObbMounted(rawPath);
    526         } catch (RemoteException e) {
    527             throw e.rethrowFromSystemServer();
    528         }
    529     }
    530 
    531     /**
    532      * Check the mounted path of an Opaque Binary Blob (OBB) file. This will
    533      * give you the path to where you can obtain access to the internals of the
    534      * OBB.
    535      *
    536      * @param rawPath path to OBB image
    537      * @return absolute path to mounted OBB image data or <code>null</code> if
    538      *         not mounted or exception encountered trying to read status
    539      */
    540     public String getMountedObbPath(String rawPath) {
    541         Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
    542 
    543         try {
    544             return mMountService.getMountedObbPath(rawPath);
    545         } catch (RemoteException e) {
    546             throw e.rethrowFromSystemServer();
    547         }
    548     }
    549 
    550     /** {@hide} */
    551     public @NonNull List<DiskInfo> getDisks() {
    552         try {
    553             return Arrays.asList(mMountService.getDisks());
    554         } catch (RemoteException e) {
    555             throw e.rethrowFromSystemServer();
    556         }
    557     }
    558 
    559     /** {@hide} */
    560     public @Nullable DiskInfo findDiskById(String id) {
    561         Preconditions.checkNotNull(id);
    562         // TODO; go directly to service to make this faster
    563         for (DiskInfo disk : getDisks()) {
    564             if (Objects.equals(disk.id, id)) {
    565                 return disk;
    566             }
    567         }
    568         return null;
    569     }
    570 
    571     /** {@hide} */
    572     public @Nullable VolumeInfo findVolumeById(String id) {
    573         Preconditions.checkNotNull(id);
    574         // TODO; go directly to service to make this faster
    575         for (VolumeInfo vol : getVolumes()) {
    576             if (Objects.equals(vol.id, id)) {
    577                 return vol;
    578             }
    579         }
    580         return null;
    581     }
    582 
    583     /** {@hide} */
    584     public @Nullable VolumeInfo findVolumeByUuid(String fsUuid) {
    585         Preconditions.checkNotNull(fsUuid);
    586         // TODO; go directly to service to make this faster
    587         for (VolumeInfo vol : getVolumes()) {
    588             if (Objects.equals(vol.fsUuid, fsUuid)) {
    589                 return vol;
    590             }
    591         }
    592         return null;
    593     }
    594 
    595     /** {@hide} */
    596     public @Nullable VolumeRecord findRecordByUuid(String fsUuid) {
    597         Preconditions.checkNotNull(fsUuid);
    598         // TODO; go directly to service to make this faster
    599         for (VolumeRecord rec : getVolumeRecords()) {
    600             if (Objects.equals(rec.fsUuid, fsUuid)) {
    601                 return rec;
    602             }
    603         }
    604         return null;
    605     }
    606 
    607     /** {@hide} */
    608     public @Nullable VolumeInfo findPrivateForEmulated(VolumeInfo emulatedVol) {
    609         if (emulatedVol != null) {
    610             return findVolumeById(emulatedVol.getId().replace("emulated", "private"));
    611         } else {
    612             return null;
    613         }
    614     }
    615 
    616     /** {@hide} */
    617     public @Nullable VolumeInfo findEmulatedForPrivate(VolumeInfo privateVol) {
    618         if (privateVol != null) {
    619             return findVolumeById(privateVol.getId().replace("private", "emulated"));
    620         } else {
    621             return null;
    622         }
    623     }
    624 
    625     /** {@hide} */
    626     public @Nullable VolumeInfo findVolumeByQualifiedUuid(String volumeUuid) {
    627         if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) {
    628             return findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL);
    629         } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
    630             return getPrimaryPhysicalVolume();
    631         } else {
    632             return findVolumeByUuid(volumeUuid);
    633         }
    634     }
    635 
    636     /** {@hide} */
    637     public @NonNull List<VolumeInfo> getVolumes() {
    638         try {
    639             return Arrays.asList(mMountService.getVolumes(0));
    640         } catch (RemoteException e) {
    641             throw e.rethrowFromSystemServer();
    642         }
    643     }
    644 
    645     /** {@hide} */
    646     public @NonNull List<VolumeInfo> getWritablePrivateVolumes() {
    647         try {
    648             final ArrayList<VolumeInfo> res = new ArrayList<>();
    649             for (VolumeInfo vol : mMountService.getVolumes(0)) {
    650                 if (vol.getType() == VolumeInfo.TYPE_PRIVATE && vol.isMountedWritable()) {
    651                     res.add(vol);
    652                 }
    653             }
    654             return res;
    655         } catch (RemoteException e) {
    656             throw e.rethrowFromSystemServer();
    657         }
    658     }
    659 
    660     /** {@hide} */
    661     public @NonNull List<VolumeRecord> getVolumeRecords() {
    662         try {
    663             return Arrays.asList(mMountService.getVolumeRecords(0));
    664         } catch (RemoteException e) {
    665             throw e.rethrowFromSystemServer();
    666         }
    667     }
    668 
    669     /** {@hide} */
    670     public @Nullable String getBestVolumeDescription(VolumeInfo vol) {
    671         if (vol == null) return null;
    672 
    673         // Nickname always takes precedence when defined
    674         if (!TextUtils.isEmpty(vol.fsUuid)) {
    675             final VolumeRecord rec = findRecordByUuid(vol.fsUuid);
    676             if (rec != null && !TextUtils.isEmpty(rec.nickname)) {
    677                 return rec.nickname;
    678             }
    679         }
    680 
    681         if (!TextUtils.isEmpty(vol.getDescription())) {
    682             return vol.getDescription();
    683         }
    684 
    685         if (vol.disk != null) {
    686             return vol.disk.getDescription();
    687         }
    688 
    689         return null;
    690     }
    691 
    692     /** {@hide} */
    693     public @Nullable VolumeInfo getPrimaryPhysicalVolume() {
    694         final List<VolumeInfo> vols = getVolumes();
    695         for (VolumeInfo vol : vols) {
    696             if (vol.isPrimaryPhysical()) {
    697                 return vol;
    698             }
    699         }
    700         return null;
    701     }
    702 
    703     /** {@hide} */
    704     public void mount(String volId) {
    705         try {
    706             mMountService.mount(volId);
    707         } catch (RemoteException e) {
    708             throw e.rethrowFromSystemServer();
    709         }
    710     }
    711 
    712     /** {@hide} */
    713     public void unmount(String volId) {
    714         try {
    715             mMountService.unmount(volId);
    716         } catch (RemoteException e) {
    717             throw e.rethrowFromSystemServer();
    718         }
    719     }
    720 
    721     /** {@hide} */
    722     public void format(String volId) {
    723         try {
    724             mMountService.format(volId);
    725         } catch (RemoteException e) {
    726             throw e.rethrowFromSystemServer();
    727         }
    728     }
    729 
    730     /** {@hide} */
    731     public long benchmark(String volId) {
    732         try {
    733             return mMountService.benchmark(volId);
    734         } catch (RemoteException e) {
    735             throw e.rethrowFromSystemServer();
    736         }
    737     }
    738 
    739     /** {@hide} */
    740     public void partitionPublic(String diskId) {
    741         try {
    742             mMountService.partitionPublic(diskId);
    743         } catch (RemoteException e) {
    744             throw e.rethrowFromSystemServer();
    745         }
    746     }
    747 
    748     /** {@hide} */
    749     public void partitionPrivate(String diskId) {
    750         try {
    751             mMountService.partitionPrivate(diskId);
    752         } catch (RemoteException e) {
    753             throw e.rethrowFromSystemServer();
    754         }
    755     }
    756 
    757     /** {@hide} */
    758     public void partitionMixed(String diskId, int ratio) {
    759         try {
    760             mMountService.partitionMixed(diskId, ratio);
    761         } catch (RemoteException e) {
    762             throw e.rethrowFromSystemServer();
    763         }
    764     }
    765 
    766     /** {@hide} */
    767     public void wipeAdoptableDisks() {
    768         // We only wipe devices in "adoptable" locations, which are in a
    769         // long-term stable slot/location on the device, where apps have a
    770         // reasonable chance of storing sensitive data. (Apps need to go through
    771         // SAF to write to transient volumes.)
    772         final List<DiskInfo> disks = getDisks();
    773         for (DiskInfo disk : disks) {
    774             final String diskId = disk.getId();
    775             if (disk.isAdoptable()) {
    776                 Slog.d(TAG, "Found adoptable " + diskId + "; wiping");
    777                 try {
    778                     // TODO: switch to explicit wipe command when we have it,
    779                     // for now rely on the fact that vfat format does a wipe
    780                     mMountService.partitionPublic(diskId);
    781                 } catch (Exception e) {
    782                     Slog.w(TAG, "Failed to wipe " + diskId + ", but soldiering onward", e);
    783                 }
    784             } else {
    785                 Slog.d(TAG, "Ignorning non-adoptable disk " + disk.getId());
    786             }
    787         }
    788     }
    789 
    790     /** {@hide} */
    791     public void setVolumeNickname(String fsUuid, String nickname) {
    792         try {
    793             mMountService.setVolumeNickname(fsUuid, nickname);
    794         } catch (RemoteException e) {
    795             throw e.rethrowFromSystemServer();
    796         }
    797     }
    798 
    799     /** {@hide} */
    800     public void setVolumeInited(String fsUuid, boolean inited) {
    801         try {
    802             mMountService.setVolumeUserFlags(fsUuid, inited ? VolumeRecord.USER_FLAG_INITED : 0,
    803                     VolumeRecord.USER_FLAG_INITED);
    804         } catch (RemoteException e) {
    805             throw e.rethrowFromSystemServer();
    806         }
    807     }
    808 
    809     /** {@hide} */
    810     public void setVolumeSnoozed(String fsUuid, boolean snoozed) {
    811         try {
    812             mMountService.setVolumeUserFlags(fsUuid, snoozed ? VolumeRecord.USER_FLAG_SNOOZED : 0,
    813                     VolumeRecord.USER_FLAG_SNOOZED);
    814         } catch (RemoteException e) {
    815             throw e.rethrowFromSystemServer();
    816         }
    817     }
    818 
    819     /** {@hide} */
    820     public void forgetVolume(String fsUuid) {
    821         try {
    822             mMountService.forgetVolume(fsUuid);
    823         } catch (RemoteException e) {
    824             throw e.rethrowFromSystemServer();
    825         }
    826     }
    827 
    828     /**
    829      * This is not the API you're looking for.
    830      *
    831      * @see PackageManager#getPrimaryStorageCurrentVolume()
    832      * @hide
    833      */
    834     public String getPrimaryStorageUuid() {
    835         try {
    836             return mMountService.getPrimaryStorageUuid();
    837         } catch (RemoteException e) {
    838             throw e.rethrowFromSystemServer();
    839         }
    840     }
    841 
    842     /**
    843      * This is not the API you're looking for.
    844      *
    845      * @see PackageManager#movePrimaryStorage(VolumeInfo)
    846      * @hide
    847      */
    848     public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) {
    849         try {
    850             mMountService.setPrimaryStorageUuid(volumeUuid, callback);
    851         } catch (RemoteException e) {
    852             throw e.rethrowFromSystemServer();
    853         }
    854     }
    855 
    856     /**
    857      * Return the {@link StorageVolume} that contains the given file, or {@code null} if none.
    858      */
    859     public @Nullable StorageVolume getStorageVolume(File file) {
    860         return getStorageVolume(getVolumeList(), file);
    861     }
    862 
    863     /** {@hide} */
    864     public static @Nullable StorageVolume getStorageVolume(File file, int userId) {
    865         return getStorageVolume(getVolumeList(userId, 0), file);
    866     }
    867 
    868     /** {@hide} */
    869     private static @Nullable StorageVolume getStorageVolume(StorageVolume[] volumes, File file) {
    870         if (file == null) {
    871             return null;
    872         }
    873         try {
    874             file = file.getCanonicalFile();
    875         } catch (IOException ignored) {
    876             Slog.d(TAG, "Could not get canonical path for " + file);
    877             return null;
    878         }
    879         for (StorageVolume volume : volumes) {
    880             File volumeFile = volume.getPathFile();
    881             try {
    882                 volumeFile = volumeFile.getCanonicalFile();
    883             } catch (IOException ignored) {
    884                 continue;
    885             }
    886             if (FileUtils.contains(volumeFile, file)) {
    887                 return volume;
    888             }
    889         }
    890         return null;
    891     }
    892 
    893     /**
    894      * Gets the state of a volume via its mountpoint.
    895      * @hide
    896      */
    897     @Deprecated
    898     public @NonNull String getVolumeState(String mountPoint) {
    899         final StorageVolume vol = getStorageVolume(new File(mountPoint));
    900         if (vol != null) {
    901             return vol.getState();
    902         } else {
    903             return Environment.MEDIA_UNKNOWN;
    904         }
    905     }
    906 
    907     /**
    908      * Return the list of shared/external storage volumes available to the
    909      * current user. This includes both the primary shared storage device and
    910      * any attached external volumes including SD cards and USB drives.
    911      *
    912      * @see Environment#getExternalStorageDirectory()
    913      * @see StorageVolume#createAccessIntent(String)
    914      */
    915     public @NonNull List<StorageVolume> getStorageVolumes() {
    916         final ArrayList<StorageVolume> res = new ArrayList<>();
    917         Collections.addAll(res,
    918                 getVolumeList(UserHandle.myUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE));
    919         return res;
    920     }
    921 
    922     /**
    923      * Return the primary shared/external storage volume available to the
    924      * current user. This volume is the same storage device returned by
    925      * {@link Environment#getExternalStorageDirectory()} and
    926      * {@link Context#getExternalFilesDir(String)}.
    927      */
    928     public @NonNull StorageVolume getPrimaryStorageVolume() {
    929         return getVolumeList(UserHandle.myUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE)[0];
    930     }
    931 
    932     /** {@hide} */
    933     public long getPrimaryStorageSize() {
    934         for (String path : INTERNAL_STORAGE_SIZE_PATHS) {
    935             final long numberBlocks = readLong(path);
    936             if (numberBlocks > 0) {
    937                 return numberBlocks * INTERNAL_STORAGE_SECTOR_SIZE;
    938             }
    939         }
    940         return 0;
    941     }
    942 
    943     private long readLong(String path) {
    944         try (final FileInputStream fis = new FileInputStream(path);
    945                 final BufferedReader reader = new BufferedReader(new InputStreamReader(fis));) {
    946             return Long.parseLong(reader.readLine());
    947         } catch (Exception e) {
    948             Slog.w(TAG, "Could not read " + path, e);
    949             return 0;
    950         }
    951     }
    952 
    953     /** @removed */
    954     public @NonNull StorageVolume[] getVolumeList() {
    955         return getVolumeList(mContext.getUserId(), 0);
    956     }
    957 
    958     /** {@hide} */
    959     public static @NonNull StorageVolume[] getVolumeList(int userId, int flags) {
    960         final IMountService mountService = IMountService.Stub.asInterface(
    961                 ServiceManager.getService("mount"));
    962         try {
    963             String packageName = ActivityThread.currentOpPackageName();
    964             if (packageName == null) {
    965                 // Package name can be null if the activity thread is running but the app
    966                 // hasn't bound yet. In this case we fall back to the first package in the
    967                 // current UID. This works for runtime permissions as permission state is
    968                 // per UID and permission realted app ops are updated for all UID packages.
    969                 String[] packageNames = ActivityThread.getPackageManager().getPackagesForUid(
    970                         android.os.Process.myUid());
    971                 if (packageNames == null || packageNames.length <= 0) {
    972                     return new StorageVolume[0];
    973                 }
    974                 packageName = packageNames[0];
    975             }
    976             final int uid = ActivityThread.getPackageManager().getPackageUid(packageName,
    977                     PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId);
    978             if (uid <= 0) {
    979                 return new StorageVolume[0];
    980             }
    981             return mountService.getVolumeList(uid, packageName, flags);
    982         } catch (RemoteException e) {
    983             throw e.rethrowFromSystemServer();
    984         }
    985     }
    986 
    987     /**
    988      * Returns list of paths for all mountable volumes.
    989      * @hide
    990      */
    991     @Deprecated
    992     public @NonNull String[] getVolumePaths() {
    993         StorageVolume[] volumes = getVolumeList();
    994         int count = volumes.length;
    995         String[] paths = new String[count];
    996         for (int i = 0; i < count; i++) {
    997             paths[i] = volumes[i].getPath();
    998         }
    999         return paths;
   1000     }
   1001 
   1002     /** @removed */
   1003     public @NonNull StorageVolume getPrimaryVolume() {
   1004         return getPrimaryVolume(getVolumeList());
   1005     }
   1006 
   1007     /** {@hide} */
   1008     public static @NonNull StorageVolume getPrimaryVolume(StorageVolume[] volumes) {
   1009         for (StorageVolume volume : volumes) {
   1010             if (volume.isPrimary()) {
   1011                 return volume;
   1012             }
   1013         }
   1014         throw new IllegalStateException("Missing primary storage");
   1015     }
   1016 
   1017     /** {@hide} */
   1018     private static final int DEFAULT_THRESHOLD_PERCENTAGE = 10;
   1019     private static final long DEFAULT_THRESHOLD_MAX_BYTES = 500 * MB_IN_BYTES;
   1020     private static final long DEFAULT_FULL_THRESHOLD_BYTES = MB_IN_BYTES;
   1021 
   1022     /**
   1023      * Return the number of available bytes until the given path is considered
   1024      * running low on storage.
   1025      *
   1026      * @hide
   1027      */
   1028     public long getStorageBytesUntilLow(File path) {
   1029         return path.getUsableSpace() - getStorageFullBytes(path);
   1030     }
   1031 
   1032     /**
   1033      * Return the number of available bytes at which the given path is
   1034      * considered running low on storage.
   1035      *
   1036      * @hide
   1037      */
   1038     public long getStorageLowBytes(File path) {
   1039         final long lowPercent = Settings.Global.getInt(mResolver,
   1040                 Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE, DEFAULT_THRESHOLD_PERCENTAGE);
   1041         final long lowBytes = (path.getTotalSpace() * lowPercent) / 100;
   1042 
   1043         final long maxLowBytes = Settings.Global.getLong(mResolver,
   1044                 Settings.Global.SYS_STORAGE_THRESHOLD_MAX_BYTES, DEFAULT_THRESHOLD_MAX_BYTES);
   1045 
   1046         return Math.min(lowBytes, maxLowBytes);
   1047     }
   1048 
   1049     /**
   1050      * Return the number of available bytes at which the given path is
   1051      * considered full.
   1052      *
   1053      * @hide
   1054      */
   1055     public long getStorageFullBytes(File path) {
   1056         return Settings.Global.getLong(mResolver, Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES,
   1057                 DEFAULT_FULL_THRESHOLD_BYTES);
   1058     }
   1059 
   1060     /** {@hide} */
   1061     public void createUserKey(int userId, int serialNumber, boolean ephemeral) {
   1062         try {
   1063             mMountService.createUserKey(userId, serialNumber, ephemeral);
   1064         } catch (RemoteException e) {
   1065             throw e.rethrowFromSystemServer();
   1066         }
   1067     }
   1068 
   1069     /** {@hide} */
   1070     public void destroyUserKey(int userId) {
   1071         try {
   1072             mMountService.destroyUserKey(userId);
   1073         } catch (RemoteException e) {
   1074             throw e.rethrowFromSystemServer();
   1075         }
   1076     }
   1077 
   1078     /** {@hide} */
   1079     public void unlockUserKey(int userId, int serialNumber, byte[] token, byte[] secret) {
   1080         try {
   1081             mMountService.unlockUserKey(userId, serialNumber, token, secret);
   1082         } catch (RemoteException e) {
   1083             throw e.rethrowFromSystemServer();
   1084         }
   1085     }
   1086 
   1087     /** {@hide} */
   1088     public void lockUserKey(int userId) {
   1089         try {
   1090             mMountService.lockUserKey(userId);
   1091         } catch (RemoteException e) {
   1092             throw e.rethrowFromSystemServer();
   1093         }
   1094     }
   1095 
   1096     /** {@hide} */
   1097     public void prepareUserStorage(String volumeUuid, int userId, int serialNumber, int flags) {
   1098         try {
   1099             mMountService.prepareUserStorage(volumeUuid, userId, serialNumber, flags);
   1100         } catch (RemoteException e) {
   1101             throw e.rethrowFromSystemServer();
   1102         }
   1103     }
   1104 
   1105     /** {@hide} */
   1106     public void destroyUserStorage(String volumeUuid, int userId, int flags) {
   1107         try {
   1108             mMountService.destroyUserStorage(volumeUuid, userId, flags);
   1109         } catch (RemoteException e) {
   1110             throw e.rethrowFromSystemServer();
   1111         }
   1112     }
   1113 
   1114     /** {@hide} */
   1115     public static boolean isUserKeyUnlocked(int userId) {
   1116         if (sMountService == null) {
   1117             sMountService = IMountService.Stub
   1118                     .asInterface(ServiceManager.getService("mount"));
   1119         }
   1120         if (sMountService == null) {
   1121             Slog.w(TAG, "Early during boot, assuming locked");
   1122             return false;
   1123         }
   1124         final long token = Binder.clearCallingIdentity();
   1125         try {
   1126             return sMountService.isUserKeyUnlocked(userId);
   1127         } catch (RemoteException e) {
   1128             throw e.rethrowAsRuntimeException();
   1129         } finally {
   1130             Binder.restoreCallingIdentity(token);
   1131         }
   1132     }
   1133 
   1134     /**
   1135      * Return if data stored at or under the given path will be encrypted while
   1136      * at rest. This can help apps avoid the overhead of double-encrypting data.
   1137      */
   1138     public boolean isEncrypted(File file) {
   1139         if (FileUtils.contains(Environment.getDataDirectory(), file)) {
   1140             return isEncrypted();
   1141         } else if (FileUtils.contains(Environment.getExpandDirectory(), file)) {
   1142             return true;
   1143         }
   1144         // TODO: extend to support shared storage
   1145         return false;
   1146     }
   1147 
   1148     /** {@hide}
   1149      * Is this device encryptable or already encrypted?
   1150      * @return true for encryptable or encrypted
   1151      *         false not encrypted and not encryptable
   1152      */
   1153     public static boolean isEncryptable() {
   1154         final String state = SystemProperties.get("ro.crypto.state", "unsupported");
   1155         return !"unsupported".equalsIgnoreCase(state);
   1156     }
   1157 
   1158     /** {@hide}
   1159      * Is this device already encrypted?
   1160      * @return true for encrypted. (Implies isEncryptable() == true)
   1161      *         false not encrypted
   1162      */
   1163     public static boolean isEncrypted() {
   1164         final String state = SystemProperties.get("ro.crypto.state", "");
   1165         return "encrypted".equalsIgnoreCase(state);
   1166     }
   1167 
   1168     /** {@hide}
   1169      * Is this device file encrypted?
   1170      * @return true for file encrypted. (Implies isEncrypted() == true)
   1171      *         false not encrypted or block encrypted
   1172      */
   1173     public static boolean isFileEncryptedNativeOnly() {
   1174         if (!isEncrypted()) {
   1175             return false;
   1176         }
   1177 
   1178         final String status = SystemProperties.get("ro.crypto.type", "");
   1179         return "file".equalsIgnoreCase(status);
   1180     }
   1181 
   1182     /** {@hide}
   1183      * Is this device block encrypted?
   1184      * @return true for block encrypted. (Implies isEncrypted() == true)
   1185      *         false not encrypted or file encrypted
   1186      */
   1187     public static boolean isBlockEncrypted() {
   1188         if (!isEncrypted()) {
   1189             return false;
   1190         }
   1191         final String status = SystemProperties.get("ro.crypto.type", "");
   1192         return "block".equalsIgnoreCase(status);
   1193     }
   1194 
   1195     /** {@hide}
   1196      * Is this device block encrypted with credentials?
   1197      * @return true for crediential block encrypted.
   1198      *         (Implies isBlockEncrypted() == true)
   1199      *         false not encrypted, file encrypted or default block encrypted
   1200      */
   1201     public static boolean isNonDefaultBlockEncrypted() {
   1202         if (!isBlockEncrypted()) {
   1203             return false;
   1204         }
   1205 
   1206         try {
   1207             IMountService mountService = IMountService.Stub.asInterface(
   1208                     ServiceManager.getService("mount"));
   1209             return mountService.getPasswordType() != CRYPT_TYPE_DEFAULT;
   1210         } catch (RemoteException e) {
   1211             Log.e(TAG, "Error getting encryption type");
   1212             return false;
   1213         }
   1214     }
   1215 
   1216     /** {@hide}
   1217      * Is this device in the process of being block encrypted?
   1218      * @return true for encrypting.
   1219      *         false otherwise
   1220      * Whether device isEncrypted at this point is undefined
   1221      * Note that only system services and CryptKeeper will ever see this return
   1222      * true - no app will ever be launched in this state.
   1223      * Also note that this state will not change without a teardown of the
   1224      * framework, so no service needs to check for changes during their lifespan
   1225      */
   1226     public static boolean isBlockEncrypting() {
   1227         final String state = SystemProperties.get("vold.encrypt_progress", "");
   1228         return !"".equalsIgnoreCase(state);
   1229     }
   1230 
   1231     /** {@hide}
   1232      * Is this device non default block encrypted and in the process of
   1233      * prompting for credentials?
   1234      * @return true for prompting for credentials.
   1235      *         (Implies isNonDefaultBlockEncrypted() == true)
   1236      *         false otherwise
   1237      * Note that only system services and CryptKeeper will ever see this return
   1238      * true - no app will ever be launched in this state.
   1239      * Also note that this state will not change without a teardown of the
   1240      * framework, so no service needs to check for changes during their lifespan
   1241      */
   1242     public static boolean inCryptKeeperBounce() {
   1243         final String status = SystemProperties.get("vold.decrypt");
   1244         return "trigger_restart_min_framework".equals(status);
   1245     }
   1246 
   1247     /** {@hide} */
   1248     public static boolean isFileEncryptedEmulatedOnly() {
   1249         return SystemProperties.getBoolean(StorageManager.PROP_EMULATE_FBE, false);
   1250     }
   1251 
   1252     /** {@hide}
   1253      * Is this device running in a file encrypted mode, either native or emulated?
   1254      * @return true for file encrypted, false otherwise
   1255      */
   1256     public static boolean isFileEncryptedNativeOrEmulated() {
   1257         return isFileEncryptedNativeOnly()
   1258                || isFileEncryptedEmulatedOnly();
   1259     }
   1260 
   1261     /** {@hide} */
   1262     public static File maybeTranslateEmulatedPathToInternal(File path) {
   1263         final IMountService mountService = IMountService.Stub.asInterface(
   1264                 ServiceManager.getService("mount"));
   1265         try {
   1266             final VolumeInfo[] vols = mountService.getVolumes(0);
   1267             for (VolumeInfo vol : vols) {
   1268                 if ((vol.getType() == VolumeInfo.TYPE_EMULATED
   1269                         || vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.isMountedReadable()) {
   1270                     final File internalPath = FileUtils.rewriteAfterRename(vol.getPath(),
   1271                             vol.getInternalPath(), path);
   1272                     if (internalPath != null && internalPath.exists()) {
   1273                         return internalPath;
   1274                     }
   1275                 }
   1276             }
   1277         } catch (RemoteException e) {
   1278             throw e.rethrowFromSystemServer();
   1279         }
   1280         return path;
   1281     }
   1282 
   1283     /** {@hide} */
   1284     public ParcelFileDescriptor mountAppFuse(String name) {
   1285         try {
   1286             return mMountService.mountAppFuse(name);
   1287         } catch (RemoteException e) {
   1288             throw e.rethrowFromSystemServer();
   1289         }
   1290     }
   1291 
   1292     /// Consts to match the password types in cryptfs.h
   1293     /** @hide */
   1294     public static final int CRYPT_TYPE_PASSWORD = 0;
   1295     /** @hide */
   1296     public static final int CRYPT_TYPE_DEFAULT = 1;
   1297     /** @hide */
   1298     public static final int CRYPT_TYPE_PATTERN = 2;
   1299     /** @hide */
   1300     public static final int CRYPT_TYPE_PIN = 3;
   1301 
   1302     // Constants for the data available via MountService.getField.
   1303     /** @hide */
   1304     public static final String SYSTEM_LOCALE_KEY = "SystemLocale";
   1305     /** @hide */
   1306     public static final String OWNER_INFO_KEY = "OwnerInfo";
   1307     /** @hide */
   1308     public static final String PATTERN_VISIBLE_KEY = "PatternVisible";
   1309     /** @hide */
   1310     public static final String PASSWORD_VISIBLE_KEY = "PasswordVisible";
   1311 }
   1312