StorageManager.java revision ddff807b762a8a455287abc97aea8f97b98fb104
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
17package android.os.storage;
18
19import static android.net.TrafficStats.GB_IN_BYTES;
20import static android.net.TrafficStats.MB_IN_BYTES;
21
22import android.annotation.BytesLong;
23import android.annotation.IntDef;
24import android.annotation.NonNull;
25import android.annotation.Nullable;
26import android.annotation.RequiresPermission;
27import android.annotation.SdkConstant;
28import android.annotation.SystemApi;
29import android.annotation.WorkerThread;
30import android.app.ActivityThread;
31import android.content.ContentResolver;
32import android.content.Context;
33import android.content.Intent;
34import android.content.pm.ApplicationInfo;
35import android.content.pm.IPackageMoveObserver;
36import android.content.pm.PackageManager;
37import android.os.Binder;
38import android.os.Environment;
39import android.os.FileUtils;
40import android.os.Handler;
41import android.os.Looper;
42import android.os.Message;
43import android.os.ParcelFileDescriptor;
44import android.os.ParcelableException;
45import android.os.ProxyFileDescriptorCallback;
46import android.os.RemoteException;
47import android.os.ServiceManager;
48import android.os.ServiceManager.ServiceNotFoundException;
49import android.os.SystemProperties;
50import android.os.UserHandle;
51import android.provider.Settings;
52import android.system.ErrnoException;
53import android.system.Os;
54import android.system.OsConstants;
55import android.text.TextUtils;
56import android.util.Log;
57import android.util.Pair;
58import android.util.Slog;
59import android.util.SparseArray;
60
61import com.android.internal.annotations.GuardedBy;
62import com.android.internal.annotations.VisibleForTesting;
63import com.android.internal.logging.MetricsLogger;
64import com.android.internal.os.AppFuseMount;
65import com.android.internal.os.FuseAppLoop;
66import com.android.internal.os.FuseUnavailableMountException;
67import com.android.internal.os.RoSystemProperties;
68import com.android.internal.os.SomeArgs;
69import com.android.internal.util.Preconditions;
70
71import java.io.File;
72import java.io.FileDescriptor;
73import java.io.FileNotFoundException;
74import java.io.IOException;
75import java.lang.annotation.Retention;
76import java.lang.annotation.RetentionPolicy;
77import java.lang.ref.WeakReference;
78import java.nio.charset.StandardCharsets;
79import java.util.ArrayList;
80import java.util.Arrays;
81import java.util.Collections;
82import java.util.Iterator;
83import java.util.List;
84import java.util.Objects;
85import java.util.UUID;
86import java.util.concurrent.ThreadFactory;
87import java.util.concurrent.atomic.AtomicInteger;
88
89/**
90 * StorageManager is the interface to the systems storage service. The storage
91 * manager handles storage-related items such as Opaque Binary Blobs (OBBs).
92 * <p>
93 * OBBs contain a filesystem that maybe be encrypted on disk and mounted
94 * on-demand from an application. OBBs are a good way of providing large amounts
95 * of binary assets without packaging them into APKs as they may be multiple
96 * gigabytes in size. However, due to their size, they're most likely stored in
97 * a shared storage pool accessible from all programs. The system does not
98 * guarantee the security of the OBB file itself: if any program modifies the
99 * OBB, there is no guarantee that a read from that OBB will produce the
100 * expected output.
101 * <p>
102 * Get an instance of this class by calling
103 * {@link android.content.Context#getSystemService(java.lang.String)} with an
104 * argument of {@link android.content.Context#STORAGE_SERVICE}.
105 */
106public class StorageManager {
107    private static final String TAG = "StorageManager";
108
109    /** {@hide} */
110    public static final String PROP_PRIMARY_PHYSICAL = "ro.vold.primary_physical";
111    /** {@hide} */
112    public static final String PROP_HAS_ADOPTABLE = "vold.has_adoptable";
113    /** {@hide} */
114    public static final String PROP_FORCE_ADOPTABLE = "persist.fw.force_adoptable";
115    /** {@hide} */
116    public static final String PROP_EMULATE_FBE = "persist.sys.emulate_fbe";
117    /** {@hide} */
118    public static final String PROP_SDCARDFS = "persist.sys.sdcardfs";
119    /** {@hide} */
120    public static final String PROP_VIRTUAL_DISK = "persist.sys.virtual_disk";
121
122    /** {@hide} */
123    public static final String UUID_PRIVATE_INTERNAL = null;
124    /** {@hide} */
125    public static final String UUID_PRIMARY_PHYSICAL = "primary_physical";
126    /** {@hide} */
127    public static final String UUID_SYSTEM = "system";
128
129    // NOTE: UUID constants below are namespaced
130    // uuid -v5 ad99aa3d-308e-4191-a200-ebcab371c0ad default
131    // uuid -v5 ad99aa3d-308e-4191-a200-ebcab371c0ad primary_physical
132    // uuid -v5 ad99aa3d-308e-4191-a200-ebcab371c0ad system
133
134    /**
135     * UUID representing the default internal storage of this device which
136     * provides {@link Environment#getDataDirectory()}.
137     * <p>
138     * This value is constant across all devices and it will never change, and
139     * thus it cannot be used to uniquely identify a particular physical device.
140     *
141     * @see #getUuidForPath(File)
142     * @see ApplicationInfo#storageUuid
143     */
144    public static final UUID UUID_DEFAULT = UUID
145            .fromString("41217664-9172-527a-b3d5-edabb50a7d69");
146
147    /** {@hide} */
148    public static final UUID UUID_PRIMARY_PHYSICAL_ = UUID
149            .fromString("0f95a519-dae7-5abf-9519-fbd6209e05fd");
150
151    /** {@hide} */
152    public static final UUID UUID_SYSTEM_ = UUID
153            .fromString("5d258386-e60d-59e3-826d-0089cdd42cc0");
154
155    /**
156     * Activity Action: Allows the user to manage their storage. This activity
157     * provides the ability to free up space on the device by deleting data such
158     * as apps.
159     * <p>
160     * If the sending application has a specific storage device or allocation
161     * size in mind, they can optionally define {@link #EXTRA_UUID} or
162     * {@link #EXTRA_REQUESTED_BYTES}, respectively.
163     */
164    @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
165    public static final String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE";
166
167    /**
168     * Extra {@link UUID} used to indicate the storage volume where an
169     * application is interested in allocating or managing disk space.
170     *
171     * @see #ACTION_MANAGE_STORAGE
172     * @see #UUID_DEFAULT
173     * @see #getUuidForPath(File)
174     * @see Intent#putExtra(String, java.io.Serializable)
175     */
176    public static final String EXTRA_UUID = "android.os.storage.extra.UUID";
177
178    /**
179     * Extra used to indicate the total size (in bytes) that an application is
180     * interested in allocating.
181     * <p>
182     * When defined, the management UI will help guide the user to free up
183     * enough disk space to reach this requested value.
184     *
185     * @see #ACTION_MANAGE_STORAGE
186     */
187    public static final String EXTRA_REQUESTED_BYTES = "android.os.storage.extra.REQUESTED_BYTES";
188
189    /** {@hide} */
190    public static final int DEBUG_FORCE_ADOPTABLE = 1 << 0;
191    /** {@hide} */
192    public static final int DEBUG_EMULATE_FBE = 1 << 1;
193    /** {@hide} */
194    public static final int DEBUG_SDCARDFS_FORCE_ON = 1 << 2;
195    /** {@hide} */
196    public static final int DEBUG_SDCARDFS_FORCE_OFF = 1 << 3;
197    /** {@hide} */
198    public static final int DEBUG_VIRTUAL_DISK = 1 << 4;
199
200    // NOTE: keep in sync with installd
201    /** {@hide} */
202    public static final int FLAG_STORAGE_DE = 1 << 0;
203    /** {@hide} */
204    public static final int FLAG_STORAGE_CE = 1 << 1;
205
206    /** {@hide} */
207    public static final int FLAG_FOR_WRITE = 1 << 8;
208    /** {@hide} */
209    public static final int FLAG_REAL_STATE = 1 << 9;
210    /** {@hide} */
211    public static final int FLAG_INCLUDE_INVISIBLE = 1 << 10;
212
213    /** {@hide} */
214    public static final int FSTRIM_FLAG_DEEP = 1 << 0;
215    /** {@hide} */
216    public static final int FSTRIM_FLAG_BENCHMARK = 1 << 1;
217
218    /** @hide The volume is not encrypted. */
219    public static final int ENCRYPTION_STATE_NONE = 1;
220
221    /** @hide The volume has been encrypted succesfully. */
222    public static final int ENCRYPTION_STATE_OK = 0;
223
224    /** @hide The volume is in a bad state.*/
225    public static final int ENCRYPTION_STATE_ERROR_UNKNOWN = -1;
226
227    /** @hide Encryption is incomplete */
228    public static final int ENCRYPTION_STATE_ERROR_INCOMPLETE = -2;
229
230    /** @hide Encryption is incomplete and irrecoverable */
231    public static final int ENCRYPTION_STATE_ERROR_INCONSISTENT = -3;
232
233    /** @hide Underlying data is corrupt */
234    public static final int ENCRYPTION_STATE_ERROR_CORRUPT = -4;
235
236    private static volatile IStorageManager sStorageManager = null;
237
238    private final Context mContext;
239    private final ContentResolver mResolver;
240
241    private final IStorageManager mStorageManager;
242    private final Looper mLooper;
243    private final AtomicInteger mNextNonce = new AtomicInteger(0);
244
245    private final ArrayList<StorageEventListenerDelegate> mDelegates = new ArrayList<>();
246
247    private static class StorageEventListenerDelegate extends IStorageEventListener.Stub implements
248            Handler.Callback {
249        private static final int MSG_STORAGE_STATE_CHANGED = 1;
250        private static final int MSG_VOLUME_STATE_CHANGED = 2;
251        private static final int MSG_VOLUME_RECORD_CHANGED = 3;
252        private static final int MSG_VOLUME_FORGOTTEN = 4;
253        private static final int MSG_DISK_SCANNED = 5;
254        private static final int MSG_DISK_DESTROYED = 6;
255
256        final StorageEventListener mCallback;
257        final Handler mHandler;
258
259        public StorageEventListenerDelegate(StorageEventListener callback, Looper looper) {
260            mCallback = callback;
261            mHandler = new Handler(looper, this);
262        }
263
264        @Override
265        public boolean handleMessage(Message msg) {
266            final SomeArgs args = (SomeArgs) msg.obj;
267            switch (msg.what) {
268                case MSG_STORAGE_STATE_CHANGED:
269                    mCallback.onStorageStateChanged((String) args.arg1, (String) args.arg2,
270                            (String) args.arg3);
271                    args.recycle();
272                    return true;
273                case MSG_VOLUME_STATE_CHANGED:
274                    mCallback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3);
275                    args.recycle();
276                    return true;
277                case MSG_VOLUME_RECORD_CHANGED:
278                    mCallback.onVolumeRecordChanged((VolumeRecord) args.arg1);
279                    args.recycle();
280                    return true;
281                case MSG_VOLUME_FORGOTTEN:
282                    mCallback.onVolumeForgotten((String) args.arg1);
283                    args.recycle();
284                    return true;
285                case MSG_DISK_SCANNED:
286                    mCallback.onDiskScanned((DiskInfo) args.arg1, args.argi2);
287                    args.recycle();
288                    return true;
289                case MSG_DISK_DESTROYED:
290                    mCallback.onDiskDestroyed((DiskInfo) args.arg1);
291                    args.recycle();
292                    return true;
293            }
294            args.recycle();
295            return false;
296        }
297
298        @Override
299        public void onUsbMassStorageConnectionChanged(boolean connected) throws RemoteException {
300            // Ignored
301        }
302
303        @Override
304        public void onStorageStateChanged(String path, String oldState, String newState) {
305            final SomeArgs args = SomeArgs.obtain();
306            args.arg1 = path;
307            args.arg2 = oldState;
308            args.arg3 = newState;
309            mHandler.obtainMessage(MSG_STORAGE_STATE_CHANGED, args).sendToTarget();
310        }
311
312        @Override
313        public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
314            final SomeArgs args = SomeArgs.obtain();
315            args.arg1 = vol;
316            args.argi2 = oldState;
317            args.argi3 = newState;
318            mHandler.obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget();
319        }
320
321        @Override
322        public void onVolumeRecordChanged(VolumeRecord rec) {
323            final SomeArgs args = SomeArgs.obtain();
324            args.arg1 = rec;
325            mHandler.obtainMessage(MSG_VOLUME_RECORD_CHANGED, args).sendToTarget();
326        }
327
328        @Override
329        public void onVolumeForgotten(String fsUuid) {
330            final SomeArgs args = SomeArgs.obtain();
331            args.arg1 = fsUuid;
332            mHandler.obtainMessage(MSG_VOLUME_FORGOTTEN, args).sendToTarget();
333        }
334
335        @Override
336        public void onDiskScanned(DiskInfo disk, int volumeCount) {
337            final SomeArgs args = SomeArgs.obtain();
338            args.arg1 = disk;
339            args.argi2 = volumeCount;
340            mHandler.obtainMessage(MSG_DISK_SCANNED, args).sendToTarget();
341        }
342
343        @Override
344        public void onDiskDestroyed(DiskInfo disk) throws RemoteException {
345            final SomeArgs args = SomeArgs.obtain();
346            args.arg1 = disk;
347            mHandler.obtainMessage(MSG_DISK_DESTROYED, args).sendToTarget();
348        }
349    }
350
351    /**
352     * Binder listener for OBB action results.
353     */
354    private final ObbActionListener mObbActionListener = new ObbActionListener();
355
356    private class ObbActionListener extends IObbActionListener.Stub {
357        @SuppressWarnings("hiding")
358        private SparseArray<ObbListenerDelegate> mListeners = new SparseArray<ObbListenerDelegate>();
359
360        @Override
361        public void onObbResult(String filename, int nonce, int status) {
362            final ObbListenerDelegate delegate;
363            synchronized (mListeners) {
364                delegate = mListeners.get(nonce);
365                if (delegate != null) {
366                    mListeners.remove(nonce);
367                }
368            }
369
370            if (delegate != null) {
371                delegate.sendObbStateChanged(filename, status);
372            }
373        }
374
375        public int addListener(OnObbStateChangeListener listener) {
376            final ObbListenerDelegate delegate = new ObbListenerDelegate(listener);
377
378            synchronized (mListeners) {
379                mListeners.put(delegate.nonce, delegate);
380            }
381
382            return delegate.nonce;
383        }
384    }
385
386    private int getNextNonce() {
387        return mNextNonce.getAndIncrement();
388    }
389
390    /**
391     * Private class containing sender and receiver code for StorageEvents.
392     */
393    private class ObbListenerDelegate {
394        private final WeakReference<OnObbStateChangeListener> mObbEventListenerRef;
395        private final Handler mHandler;
396
397        private final int nonce;
398
399        ObbListenerDelegate(OnObbStateChangeListener listener) {
400            nonce = getNextNonce();
401            mObbEventListenerRef = new WeakReference<OnObbStateChangeListener>(listener);
402            mHandler = new Handler(mLooper) {
403                @Override
404                public void handleMessage(Message msg) {
405                    final OnObbStateChangeListener changeListener = getListener();
406                    if (changeListener == null) {
407                        return;
408                    }
409
410                    changeListener.onObbStateChange((String) msg.obj, msg.arg1);
411                }
412            };
413        }
414
415        OnObbStateChangeListener getListener() {
416            if (mObbEventListenerRef == null) {
417                return null;
418            }
419            return mObbEventListenerRef.get();
420        }
421
422        void sendObbStateChanged(String path, int state) {
423            mHandler.obtainMessage(0, state, 0, path).sendToTarget();
424        }
425    }
426
427    /** {@hide} */
428    @Deprecated
429    public static StorageManager from(Context context) {
430        return context.getSystemService(StorageManager.class);
431    }
432
433    /**
434     * Constructs a StorageManager object through which an application can
435     * can communicate with the systems mount service.
436     *
437     * @param looper The {@link android.os.Looper} which events will be received on.
438     *
439     * <p>Applications can get instance of this class by calling
440     * {@link android.content.Context#getSystemService(java.lang.String)} with an argument
441     * of {@link android.content.Context#STORAGE_SERVICE}.
442     *
443     * @hide
444     */
445    public StorageManager(Context context, Looper looper) throws ServiceNotFoundException {
446        mContext = context;
447        mResolver = context.getContentResolver();
448        mLooper = looper;
449        mStorageManager = IStorageManager.Stub.asInterface(ServiceManager.getServiceOrThrow("mount"));
450    }
451
452    /**
453     * Registers a {@link android.os.storage.StorageEventListener StorageEventListener}.
454     *
455     * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object.
456     *
457     * @hide
458     */
459    public void registerListener(StorageEventListener listener) {
460        synchronized (mDelegates) {
461            final StorageEventListenerDelegate delegate = new StorageEventListenerDelegate(listener,
462                    mLooper);
463            try {
464                mStorageManager.registerListener(delegate);
465            } catch (RemoteException e) {
466                throw e.rethrowFromSystemServer();
467            }
468            mDelegates.add(delegate);
469        }
470    }
471
472    /**
473     * Unregisters a {@link android.os.storage.StorageEventListener StorageEventListener}.
474     *
475     * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object.
476     *
477     * @hide
478     */
479    public void unregisterListener(StorageEventListener listener) {
480        synchronized (mDelegates) {
481            for (Iterator<StorageEventListenerDelegate> i = mDelegates.iterator(); i.hasNext();) {
482                final StorageEventListenerDelegate delegate = i.next();
483                if (delegate.mCallback == listener) {
484                    try {
485                        mStorageManager.unregisterListener(delegate);
486                    } catch (RemoteException e) {
487                        throw e.rethrowFromSystemServer();
488                    }
489                    i.remove();
490                }
491            }
492        }
493    }
494
495    /**
496     * Enables USB Mass Storage (UMS) on the device.
497     *
498     * @hide
499     */
500    @Deprecated
501    public void enableUsbMassStorage() {
502    }
503
504    /**
505     * Disables USB Mass Storage (UMS) on the device.
506     *
507     * @hide
508     */
509    @Deprecated
510    public void disableUsbMassStorage() {
511    }
512
513    /**
514     * Query if a USB Mass Storage (UMS) host is connected.
515     * @return true if UMS host is connected.
516     *
517     * @hide
518     */
519    @Deprecated
520    public boolean isUsbMassStorageConnected() {
521        return false;
522    }
523
524    /**
525     * Query if a USB Mass Storage (UMS) is enabled on the device.
526     * @return true if UMS host is enabled.
527     *
528     * @hide
529     */
530    @Deprecated
531    public boolean isUsbMassStorageEnabled() {
532        return false;
533    }
534
535    /**
536     * Mount an Opaque Binary Blob (OBB) file. If a <code>key</code> is
537     * specified, it is supplied to the mounting process to be used in any
538     * encryption used in the OBB.
539     * <p>
540     * The OBB will remain mounted for as long as the StorageManager reference
541     * is held by the application. As soon as this reference is lost, the OBBs
542     * in use will be unmounted. The {@link OnObbStateChangeListener} registered
543     * with this call will receive the success or failure of this operation.
544     * <p>
545     * <em>Note:</em> you can only mount OBB files for which the OBB tag on the
546     * file matches a package ID that is owned by the calling program's UID.
547     * That is, shared UID applications can attempt to mount any other
548     * application's OBB that shares its UID.
549     *
550     * @param rawPath the path to the OBB file
551     * @param key secret used to encrypt the OBB; may be <code>null</code> if no
552     *            encryption was used on the OBB.
553     * @param listener will receive the success or failure of the operation
554     * @return whether the mount call was successfully queued or not
555     */
556    public boolean mountObb(String rawPath, String key, OnObbStateChangeListener listener) {
557        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
558        Preconditions.checkNotNull(listener, "listener cannot be null");
559
560        try {
561            final String canonicalPath = new File(rawPath).getCanonicalPath();
562            final int nonce = mObbActionListener.addListener(listener);
563            mStorageManager.mountObb(rawPath, canonicalPath, key, mObbActionListener, nonce);
564            return true;
565        } catch (IOException e) {
566            throw new IllegalArgumentException("Failed to resolve path: " + rawPath, e);
567        } catch (RemoteException e) {
568            throw e.rethrowFromSystemServer();
569        }
570    }
571
572    /**
573     * Unmount an Opaque Binary Blob (OBB) file asynchronously. If the
574     * <code>force</code> flag is true, it will kill any application needed to
575     * unmount the given OBB (even the calling application).
576     * <p>
577     * The {@link OnObbStateChangeListener} registered with this call will
578     * receive the success or failure of this operation.
579     * <p>
580     * <em>Note:</em> you can only mount OBB files for which the OBB tag on the
581     * file matches a package ID that is owned by the calling program's UID.
582     * That is, shared UID applications can obtain access to any other
583     * application's OBB that shares its UID.
584     * <p>
585     *
586     * @param rawPath path to the OBB file
587     * @param force whether to kill any programs using this in order to unmount
588     *            it
589     * @param listener will receive the success or failure of the operation
590     * @return whether the unmount call was successfully queued or not
591     */
592    public boolean unmountObb(String rawPath, boolean force, OnObbStateChangeListener listener) {
593        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
594        Preconditions.checkNotNull(listener, "listener cannot be null");
595
596        try {
597            final int nonce = mObbActionListener.addListener(listener);
598            mStorageManager.unmountObb(rawPath, force, mObbActionListener, nonce);
599            return true;
600        } catch (RemoteException e) {
601            throw e.rethrowFromSystemServer();
602        }
603    }
604
605    /**
606     * Check whether an Opaque Binary Blob (OBB) is mounted or not.
607     *
608     * @param rawPath path to OBB image
609     * @return true if OBB is mounted; false if not mounted or on error
610     */
611    public boolean isObbMounted(String rawPath) {
612        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
613
614        try {
615            return mStorageManager.isObbMounted(rawPath);
616        } catch (RemoteException e) {
617            throw e.rethrowFromSystemServer();
618        }
619    }
620
621    /**
622     * Check the mounted path of an Opaque Binary Blob (OBB) file. This will
623     * give you the path to where you can obtain access to the internals of the
624     * OBB.
625     *
626     * @param rawPath path to OBB image
627     * @return absolute path to mounted OBB image data or <code>null</code> if
628     *         not mounted or exception encountered trying to read status
629     */
630    public String getMountedObbPath(String rawPath) {
631        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
632
633        try {
634            return mStorageManager.getMountedObbPath(rawPath);
635        } catch (RemoteException e) {
636            throw e.rethrowFromSystemServer();
637        }
638    }
639
640    /** {@hide} */
641    public @NonNull List<DiskInfo> getDisks() {
642        try {
643            return Arrays.asList(mStorageManager.getDisks());
644        } catch (RemoteException e) {
645            throw e.rethrowFromSystemServer();
646        }
647    }
648
649    /** {@hide} */
650    public @Nullable DiskInfo findDiskById(String id) {
651        Preconditions.checkNotNull(id);
652        // TODO; go directly to service to make this faster
653        for (DiskInfo disk : getDisks()) {
654            if (Objects.equals(disk.id, id)) {
655                return disk;
656            }
657        }
658        return null;
659    }
660
661    /** {@hide} */
662    public @Nullable VolumeInfo findVolumeById(String id) {
663        Preconditions.checkNotNull(id);
664        // TODO; go directly to service to make this faster
665        for (VolumeInfo vol : getVolumes()) {
666            if (Objects.equals(vol.id, id)) {
667                return vol;
668            }
669        }
670        return null;
671    }
672
673    /** {@hide} */
674    public @Nullable VolumeInfo findVolumeByUuid(String fsUuid) {
675        Preconditions.checkNotNull(fsUuid);
676        // TODO; go directly to service to make this faster
677        for (VolumeInfo vol : getVolumes()) {
678            if (Objects.equals(vol.fsUuid, fsUuid)) {
679                return vol;
680            }
681        }
682        return null;
683    }
684
685    /** {@hide} */
686    public @Nullable VolumeRecord findRecordByUuid(String fsUuid) {
687        Preconditions.checkNotNull(fsUuid);
688        // TODO; go directly to service to make this faster
689        for (VolumeRecord rec : getVolumeRecords()) {
690            if (Objects.equals(rec.fsUuid, fsUuid)) {
691                return rec;
692            }
693        }
694        return null;
695    }
696
697    /** {@hide} */
698    public @Nullable VolumeInfo findPrivateForEmulated(VolumeInfo emulatedVol) {
699        if (emulatedVol != null) {
700            return findVolumeById(emulatedVol.getId().replace("emulated", "private"));
701        } else {
702            return null;
703        }
704    }
705
706    /** {@hide} */
707    public @Nullable VolumeInfo findEmulatedForPrivate(VolumeInfo privateVol) {
708        if (privateVol != null) {
709            return findVolumeById(privateVol.getId().replace("private", "emulated"));
710        } else {
711            return null;
712        }
713    }
714
715    /** {@hide} */
716    public @Nullable VolumeInfo findVolumeByQualifiedUuid(String volumeUuid) {
717        if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) {
718            return findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL);
719        } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
720            return getPrimaryPhysicalVolume();
721        } else {
722            return findVolumeByUuid(volumeUuid);
723        }
724    }
725
726    /**
727     * Return a UUID identifying the storage volume that hosts the given
728     * filesystem path.
729     * <p>
730     * If this path is hosted by the default internal storage of the device at
731     * {@link Environment#getDataDirectory()}, the returned value will be
732     * {@link #UUID_DEFAULT}.
733     *
734     * @throws IOException when the storage device at the given path isn't
735     *             present, or when it doesn't have a valid UUID.
736     */
737    public @NonNull UUID getUuidForPath(@NonNull File path) throws IOException {
738        Preconditions.checkNotNull(path);
739        final String pathString = path.getCanonicalPath();
740        if (FileUtils.contains(Environment.getDataDirectory().getAbsolutePath(), pathString)) {
741            return UUID_DEFAULT;
742        }
743        try {
744            for (VolumeInfo vol : mStorageManager.getVolumes(0)) {
745                if (vol.path != null && FileUtils.contains(vol.path, pathString)) {
746                    // TODO: verify that emulated adopted devices have UUID of
747                    // underlying volume
748                    return convert(vol.fsUuid);
749                }
750            }
751        } catch (RemoteException e) {
752            throw e.rethrowFromSystemServer();
753        }
754        throw new FileNotFoundException("Failed to find a storage device for " + path);
755    }
756
757    /** {@hide} */
758    public @NonNull File findPathForUuid(String volumeUuid) throws FileNotFoundException {
759        final VolumeInfo vol = findVolumeByQualifiedUuid(volumeUuid);
760        if (vol != null) {
761            return vol.getPath();
762        }
763        throw new FileNotFoundException("Failed to find a storage device for " + volumeUuid);
764    }
765
766    /** {@hide} */
767    public @NonNull List<VolumeInfo> getVolumes() {
768        try {
769            return Arrays.asList(mStorageManager.getVolumes(0));
770        } catch (RemoteException e) {
771            throw e.rethrowFromSystemServer();
772        }
773    }
774
775    /** {@hide} */
776    public @NonNull List<VolumeInfo> getWritablePrivateVolumes() {
777        try {
778            final ArrayList<VolumeInfo> res = new ArrayList<>();
779            for (VolumeInfo vol : mStorageManager.getVolumes(0)) {
780                if (vol.getType() == VolumeInfo.TYPE_PRIVATE && vol.isMountedWritable()) {
781                    res.add(vol);
782                }
783            }
784            return res;
785        } catch (RemoteException e) {
786            throw e.rethrowFromSystemServer();
787        }
788    }
789
790    /** {@hide} */
791    public @NonNull List<VolumeRecord> getVolumeRecords() {
792        try {
793            return Arrays.asList(mStorageManager.getVolumeRecords(0));
794        } catch (RemoteException e) {
795            throw e.rethrowFromSystemServer();
796        }
797    }
798
799    /** {@hide} */
800    public @Nullable String getBestVolumeDescription(VolumeInfo vol) {
801        if (vol == null) return null;
802
803        // Nickname always takes precedence when defined
804        if (!TextUtils.isEmpty(vol.fsUuid)) {
805            final VolumeRecord rec = findRecordByUuid(vol.fsUuid);
806            if (rec != null && !TextUtils.isEmpty(rec.nickname)) {
807                return rec.nickname;
808            }
809        }
810
811        if (!TextUtils.isEmpty(vol.getDescription())) {
812            return vol.getDescription();
813        }
814
815        if (vol.disk != null) {
816            return vol.disk.getDescription();
817        }
818
819        return null;
820    }
821
822    /** {@hide} */
823    public @Nullable VolumeInfo getPrimaryPhysicalVolume() {
824        final List<VolumeInfo> vols = getVolumes();
825        for (VolumeInfo vol : vols) {
826            if (vol.isPrimaryPhysical()) {
827                return vol;
828            }
829        }
830        return null;
831    }
832
833    /** {@hide} */
834    public void mount(String volId) {
835        try {
836            mStorageManager.mount(volId);
837        } catch (RemoteException e) {
838            throw e.rethrowFromSystemServer();
839        }
840    }
841
842    /** {@hide} */
843    public void unmount(String volId) {
844        try {
845            mStorageManager.unmount(volId);
846        } catch (RemoteException e) {
847            throw e.rethrowFromSystemServer();
848        }
849    }
850
851    /** {@hide} */
852    public void format(String volId) {
853        try {
854            mStorageManager.format(volId);
855        } catch (RemoteException e) {
856            throw e.rethrowFromSystemServer();
857        }
858    }
859
860    /** {@hide} */
861    public long benchmark(String volId) {
862        try {
863            return mStorageManager.benchmark(volId);
864        } catch (RemoteException e) {
865            throw e.rethrowFromSystemServer();
866        }
867    }
868
869    /** {@hide} */
870    public void partitionPublic(String diskId) {
871        try {
872            mStorageManager.partitionPublic(diskId);
873        } catch (RemoteException e) {
874            throw e.rethrowFromSystemServer();
875        }
876    }
877
878    /** {@hide} */
879    public void partitionPrivate(String diskId) {
880        try {
881            mStorageManager.partitionPrivate(diskId);
882        } catch (RemoteException e) {
883            throw e.rethrowFromSystemServer();
884        }
885    }
886
887    /** {@hide} */
888    public void partitionMixed(String diskId, int ratio) {
889        try {
890            mStorageManager.partitionMixed(diskId, ratio);
891        } catch (RemoteException e) {
892            throw e.rethrowFromSystemServer();
893        }
894    }
895
896    /** {@hide} */
897    public void wipeAdoptableDisks() {
898        // We only wipe devices in "adoptable" locations, which are in a
899        // long-term stable slot/location on the device, where apps have a
900        // reasonable chance of storing sensitive data. (Apps need to go through
901        // SAF to write to transient volumes.)
902        final List<DiskInfo> disks = getDisks();
903        for (DiskInfo disk : disks) {
904            final String diskId = disk.getId();
905            if (disk.isAdoptable()) {
906                Slog.d(TAG, "Found adoptable " + diskId + "; wiping");
907                try {
908                    // TODO: switch to explicit wipe command when we have it,
909                    // for now rely on the fact that vfat format does a wipe
910                    mStorageManager.partitionPublic(diskId);
911                } catch (Exception e) {
912                    Slog.w(TAG, "Failed to wipe " + diskId + ", but soldiering onward", e);
913                }
914            } else {
915                Slog.d(TAG, "Ignorning non-adoptable disk " + disk.getId());
916            }
917        }
918    }
919
920    /** {@hide} */
921    public void setVolumeNickname(String fsUuid, String nickname) {
922        try {
923            mStorageManager.setVolumeNickname(fsUuid, nickname);
924        } catch (RemoteException e) {
925            throw e.rethrowFromSystemServer();
926        }
927    }
928
929    /** {@hide} */
930    public void setVolumeInited(String fsUuid, boolean inited) {
931        try {
932            mStorageManager.setVolumeUserFlags(fsUuid, inited ? VolumeRecord.USER_FLAG_INITED : 0,
933                    VolumeRecord.USER_FLAG_INITED);
934        } catch (RemoteException e) {
935            throw e.rethrowFromSystemServer();
936        }
937    }
938
939    /** {@hide} */
940    public void setVolumeSnoozed(String fsUuid, boolean snoozed) {
941        try {
942            mStorageManager.setVolumeUserFlags(fsUuid, snoozed ? VolumeRecord.USER_FLAG_SNOOZED : 0,
943                    VolumeRecord.USER_FLAG_SNOOZED);
944        } catch (RemoteException e) {
945            throw e.rethrowFromSystemServer();
946        }
947    }
948
949    /** {@hide} */
950    public void forgetVolume(String fsUuid) {
951        try {
952            mStorageManager.forgetVolume(fsUuid);
953        } catch (RemoteException e) {
954            throw e.rethrowFromSystemServer();
955        }
956    }
957
958    /**
959     * This is not the API you're looking for.
960     *
961     * @see PackageManager#getPrimaryStorageCurrentVolume()
962     * @hide
963     */
964    public String getPrimaryStorageUuid() {
965        try {
966            return mStorageManager.getPrimaryStorageUuid();
967        } catch (RemoteException e) {
968            throw e.rethrowFromSystemServer();
969        }
970    }
971
972    /**
973     * This is not the API you're looking for.
974     *
975     * @see PackageManager#movePrimaryStorage(VolumeInfo)
976     * @hide
977     */
978    public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) {
979        try {
980            mStorageManager.setPrimaryStorageUuid(volumeUuid, callback);
981        } catch (RemoteException e) {
982            throw e.rethrowFromSystemServer();
983        }
984    }
985
986    /**
987     * Return the {@link StorageVolume} that contains the given file, or {@code null} if none.
988     */
989    public @Nullable StorageVolume getStorageVolume(File file) {
990        return getStorageVolume(getVolumeList(), file);
991    }
992
993    /** {@hide} */
994    public static @Nullable StorageVolume getStorageVolume(File file, int userId) {
995        return getStorageVolume(getVolumeList(userId, 0), file);
996    }
997
998    /** {@hide} */
999    private static @Nullable StorageVolume getStorageVolume(StorageVolume[] volumes, File file) {
1000        if (file == null) {
1001            return null;
1002        }
1003        try {
1004            file = file.getCanonicalFile();
1005        } catch (IOException ignored) {
1006            Slog.d(TAG, "Could not get canonical path for " + file);
1007            return null;
1008        }
1009        for (StorageVolume volume : volumes) {
1010            File volumeFile = volume.getPathFile();
1011            try {
1012                volumeFile = volumeFile.getCanonicalFile();
1013            } catch (IOException ignored) {
1014                continue;
1015            }
1016            if (FileUtils.contains(volumeFile, file)) {
1017                return volume;
1018            }
1019        }
1020        return null;
1021    }
1022
1023    /**
1024     * Gets the state of a volume via its mountpoint.
1025     * @hide
1026     */
1027    @Deprecated
1028    public @NonNull String getVolumeState(String mountPoint) {
1029        final StorageVolume vol = getStorageVolume(new File(mountPoint));
1030        if (vol != null) {
1031            return vol.getState();
1032        } else {
1033            return Environment.MEDIA_UNKNOWN;
1034        }
1035    }
1036
1037    /**
1038     * Return the list of shared/external storage volumes available to the
1039     * current user. This includes both the primary shared storage device and
1040     * any attached external volumes including SD cards and USB drives.
1041     *
1042     * @see Environment#getExternalStorageDirectory()
1043     * @see StorageVolume#createAccessIntent(String)
1044     */
1045    public @NonNull List<StorageVolume> getStorageVolumes() {
1046        final ArrayList<StorageVolume> res = new ArrayList<>();
1047        Collections.addAll(res,
1048                getVolumeList(UserHandle.myUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE));
1049        return res;
1050    }
1051
1052    /**
1053     * Return the primary shared/external storage volume available to the
1054     * current user. This volume is the same storage device returned by
1055     * {@link Environment#getExternalStorageDirectory()} and
1056     * {@link Context#getExternalFilesDir(String)}.
1057     */
1058    public @NonNull StorageVolume getPrimaryStorageVolume() {
1059        return getVolumeList(UserHandle.myUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE)[0];
1060    }
1061
1062    /** {@hide} */
1063    public static Pair<String, Long> getPrimaryStoragePathAndSize() {
1064        return Pair.create(null,
1065                FileUtils.roundStorageSize(Environment.getDataDirectory().getTotalSpace()));
1066    }
1067
1068    /** {@hide} */
1069    public long getPrimaryStorageSize() {
1070        return FileUtils.roundStorageSize(Environment.getDataDirectory().getTotalSpace());
1071    }
1072
1073    /** @removed */
1074    public @NonNull StorageVolume[] getVolumeList() {
1075        return getVolumeList(mContext.getUserId(), 0);
1076    }
1077
1078    /** {@hide} */
1079    public static @NonNull StorageVolume[] getVolumeList(int userId, int flags) {
1080        final IStorageManager storageManager = IStorageManager.Stub.asInterface(
1081                ServiceManager.getService("mount"));
1082        try {
1083            String packageName = ActivityThread.currentOpPackageName();
1084            if (packageName == null) {
1085                // Package name can be null if the activity thread is running but the app
1086                // hasn't bound yet. In this case we fall back to the first package in the
1087                // current UID. This works for runtime permissions as permission state is
1088                // per UID and permission realted app ops are updated for all UID packages.
1089                String[] packageNames = ActivityThread.getPackageManager().getPackagesForUid(
1090                        android.os.Process.myUid());
1091                if (packageNames == null || packageNames.length <= 0) {
1092                    return new StorageVolume[0];
1093                }
1094                packageName = packageNames[0];
1095            }
1096            final int uid = ActivityThread.getPackageManager().getPackageUid(packageName,
1097                    PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId);
1098            if (uid <= 0) {
1099                return new StorageVolume[0];
1100            }
1101            return storageManager.getVolumeList(uid, packageName, flags);
1102        } catch (RemoteException e) {
1103            throw e.rethrowFromSystemServer();
1104        }
1105    }
1106
1107    /**
1108     * Returns list of paths for all mountable volumes.
1109     * @hide
1110     */
1111    @Deprecated
1112    public @NonNull String[] getVolumePaths() {
1113        StorageVolume[] volumes = getVolumeList();
1114        int count = volumes.length;
1115        String[] paths = new String[count];
1116        for (int i = 0; i < count; i++) {
1117            paths[i] = volumes[i].getPath();
1118        }
1119        return paths;
1120    }
1121
1122    /** @removed */
1123    public @NonNull StorageVolume getPrimaryVolume() {
1124        return getPrimaryVolume(getVolumeList());
1125    }
1126
1127    /** {@hide} */
1128    public static @NonNull StorageVolume getPrimaryVolume(StorageVolume[] volumes) {
1129        for (StorageVolume volume : volumes) {
1130            if (volume.isPrimary()) {
1131                return volume;
1132            }
1133        }
1134        throw new IllegalStateException("Missing primary storage");
1135    }
1136
1137    private static final int DEFAULT_THRESHOLD_PERCENTAGE = 5;
1138    private static final long DEFAULT_THRESHOLD_MAX_BYTES = 500 * MB_IN_BYTES;
1139
1140    private static final int DEFAULT_CACHE_PERCENTAGE = 10;
1141    private static final long DEFAULT_CACHE_MAX_BYTES = 5 * GB_IN_BYTES;
1142
1143    private static final long DEFAULT_FULL_THRESHOLD_BYTES = MB_IN_BYTES;
1144
1145    /**
1146     * Return the number of available bytes until the given path is considered
1147     * running low on storage.
1148     *
1149     * @hide
1150     */
1151    public long getStorageBytesUntilLow(File path) {
1152        return path.getUsableSpace() - getStorageFullBytes(path);
1153    }
1154
1155    /**
1156     * Return the number of available bytes at which the given path is
1157     * considered running low on storage.
1158     *
1159     * @hide
1160     */
1161    public long getStorageLowBytes(File path) {
1162        final long lowPercent = Settings.Global.getInt(mResolver,
1163                Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE, DEFAULT_THRESHOLD_PERCENTAGE);
1164        final long lowBytes = (path.getTotalSpace() * lowPercent) / 100;
1165
1166        final long maxLowBytes = Settings.Global.getLong(mResolver,
1167                Settings.Global.SYS_STORAGE_THRESHOLD_MAX_BYTES, DEFAULT_THRESHOLD_MAX_BYTES);
1168
1169        return Math.min(lowBytes, maxLowBytes);
1170    }
1171
1172    /**
1173     * Return the minimum number of bytes of storage on the device that should
1174     * be reserved for cached data.
1175     *
1176     * @hide
1177     */
1178    public long getStorageCacheBytes(File path) {
1179        final long cachePercent = Settings.Global.getInt(mResolver,
1180                Settings.Global.SYS_STORAGE_CACHE_PERCENTAGE, DEFAULT_CACHE_PERCENTAGE);
1181        final long cacheBytes = (path.getTotalSpace() * cachePercent) / 100;
1182
1183        final long maxCacheBytes = Settings.Global.getLong(mResolver,
1184                Settings.Global.SYS_STORAGE_CACHE_MAX_BYTES, DEFAULT_CACHE_MAX_BYTES);
1185
1186        return Math.min(cacheBytes, maxCacheBytes);
1187    }
1188
1189    /**
1190     * Return the number of available bytes at which the given path is
1191     * considered full.
1192     *
1193     * @hide
1194     */
1195    public long getStorageFullBytes(File path) {
1196        return Settings.Global.getLong(mResolver, Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES,
1197                DEFAULT_FULL_THRESHOLD_BYTES);
1198    }
1199
1200    /** {@hide} */
1201    public void createUserKey(int userId, int serialNumber, boolean ephemeral) {
1202        try {
1203            mStorageManager.createUserKey(userId, serialNumber, ephemeral);
1204        } catch (RemoteException e) {
1205            throw e.rethrowFromSystemServer();
1206        }
1207    }
1208
1209    /** {@hide} */
1210    public void destroyUserKey(int userId) {
1211        try {
1212            mStorageManager.destroyUserKey(userId);
1213        } catch (RemoteException e) {
1214            throw e.rethrowFromSystemServer();
1215        }
1216    }
1217
1218    /** {@hide} */
1219    public void unlockUserKey(int userId, int serialNumber, byte[] token, byte[] secret) {
1220        try {
1221            mStorageManager.unlockUserKey(userId, serialNumber, token, secret);
1222        } catch (RemoteException e) {
1223            throw e.rethrowFromSystemServer();
1224        }
1225    }
1226
1227    /** {@hide} */
1228    public void lockUserKey(int userId) {
1229        try {
1230            mStorageManager.lockUserKey(userId);
1231        } catch (RemoteException e) {
1232            throw e.rethrowFromSystemServer();
1233        }
1234    }
1235
1236    /** {@hide} */
1237    public void prepareUserStorage(String volumeUuid, int userId, int serialNumber, int flags) {
1238        try {
1239            mStorageManager.prepareUserStorage(volumeUuid, userId, serialNumber, flags);
1240        } catch (RemoteException e) {
1241            throw e.rethrowFromSystemServer();
1242        }
1243    }
1244
1245    /** {@hide} */
1246    public void destroyUserStorage(String volumeUuid, int userId, int flags) {
1247        try {
1248            mStorageManager.destroyUserStorage(volumeUuid, userId, flags);
1249        } catch (RemoteException e) {
1250            throw e.rethrowFromSystemServer();
1251        }
1252    }
1253
1254    /** {@hide} */
1255    public void secdiscard(String path) {
1256        try {
1257            mStorageManager.secdiscard(path);
1258        } catch (RemoteException e) {
1259            throw e.rethrowFromSystemServer();
1260        }
1261    }
1262
1263    /** {@hide} */
1264    public static boolean isUserKeyUnlocked(int userId) {
1265        if (sStorageManager == null) {
1266            sStorageManager = IStorageManager.Stub
1267                    .asInterface(ServiceManager.getService("mount"));
1268        }
1269        if (sStorageManager == null) {
1270            Slog.w(TAG, "Early during boot, assuming locked");
1271            return false;
1272        }
1273        final long token = Binder.clearCallingIdentity();
1274        try {
1275            return sStorageManager.isUserKeyUnlocked(userId);
1276        } catch (RemoteException e) {
1277            throw e.rethrowAsRuntimeException();
1278        } finally {
1279            Binder.restoreCallingIdentity(token);
1280        }
1281    }
1282
1283    /**
1284     * Return if data stored at or under the given path will be encrypted while
1285     * at rest. This can help apps avoid the overhead of double-encrypting data.
1286     */
1287    public boolean isEncrypted(File file) {
1288        if (FileUtils.contains(Environment.getDataDirectory(), file)) {
1289            return isEncrypted();
1290        } else if (FileUtils.contains(Environment.getExpandDirectory(), file)) {
1291            return true;
1292        }
1293        // TODO: extend to support shared storage
1294        return false;
1295    }
1296
1297    /** {@hide}
1298     * Is this device encryptable or already encrypted?
1299     * @return true for encryptable or encrypted
1300     *         false not encrypted and not encryptable
1301     */
1302    public static boolean isEncryptable() {
1303        return RoSystemProperties.CRYPTO_ENCRYPTABLE;
1304    }
1305
1306    /** {@hide}
1307     * Is this device already encrypted?
1308     * @return true for encrypted. (Implies isEncryptable() == true)
1309     *         false not encrypted
1310     */
1311    public static boolean isEncrypted() {
1312        return RoSystemProperties.CRYPTO_ENCRYPTED;
1313    }
1314
1315    /** {@hide}
1316     * Is this device file encrypted?
1317     * @return true for file encrypted. (Implies isEncrypted() == true)
1318     *         false not encrypted or block encrypted
1319     */
1320    public static boolean isFileEncryptedNativeOnly() {
1321        if (!isEncrypted()) {
1322            return false;
1323        }
1324        return RoSystemProperties.CRYPTO_FILE_ENCRYPTED;
1325    }
1326
1327    /** {@hide}
1328     * Is this device block encrypted?
1329     * @return true for block encrypted. (Implies isEncrypted() == true)
1330     *         false not encrypted or file encrypted
1331     */
1332    public static boolean isBlockEncrypted() {
1333        if (!isEncrypted()) {
1334            return false;
1335        }
1336        return RoSystemProperties.CRYPTO_BLOCK_ENCRYPTED;
1337    }
1338
1339    /** {@hide}
1340     * Is this device block encrypted with credentials?
1341     * @return true for crediential block encrypted.
1342     *         (Implies isBlockEncrypted() == true)
1343     *         false not encrypted, file encrypted or default block encrypted
1344     */
1345    public static boolean isNonDefaultBlockEncrypted() {
1346        if (!isBlockEncrypted()) {
1347            return false;
1348        }
1349
1350        try {
1351            IStorageManager storageManager = IStorageManager.Stub.asInterface(
1352                    ServiceManager.getService("mount"));
1353            return storageManager.getPasswordType() != CRYPT_TYPE_DEFAULT;
1354        } catch (RemoteException e) {
1355            Log.e(TAG, "Error getting encryption type");
1356            return false;
1357        }
1358    }
1359
1360    /** {@hide}
1361     * Is this device in the process of being block encrypted?
1362     * @return true for encrypting.
1363     *         false otherwise
1364     * Whether device isEncrypted at this point is undefined
1365     * Note that only system services and CryptKeeper will ever see this return
1366     * true - no app will ever be launched in this state.
1367     * Also note that this state will not change without a teardown of the
1368     * framework, so no service needs to check for changes during their lifespan
1369     */
1370    public static boolean isBlockEncrypting() {
1371        final String state = SystemProperties.get("vold.encrypt_progress", "");
1372        return !"".equalsIgnoreCase(state);
1373    }
1374
1375    /** {@hide}
1376     * Is this device non default block encrypted and in the process of
1377     * prompting for credentials?
1378     * @return true for prompting for credentials.
1379     *         (Implies isNonDefaultBlockEncrypted() == true)
1380     *         false otherwise
1381     * Note that only system services and CryptKeeper will ever see this return
1382     * true - no app will ever be launched in this state.
1383     * Also note that this state will not change without a teardown of the
1384     * framework, so no service needs to check for changes during their lifespan
1385     */
1386    public static boolean inCryptKeeperBounce() {
1387        final String status = SystemProperties.get("vold.decrypt");
1388        return "trigger_restart_min_framework".equals(status);
1389    }
1390
1391    /** {@hide} */
1392    public static boolean isFileEncryptedEmulatedOnly() {
1393        return SystemProperties.getBoolean(StorageManager.PROP_EMULATE_FBE, false);
1394    }
1395
1396    /** {@hide}
1397     * Is this device running in a file encrypted mode, either native or emulated?
1398     * @return true for file encrypted, false otherwise
1399     */
1400    public static boolean isFileEncryptedNativeOrEmulated() {
1401        return isFileEncryptedNativeOnly()
1402               || isFileEncryptedEmulatedOnly();
1403    }
1404
1405    /** {@hide} */
1406    public static File maybeTranslateEmulatedPathToInternal(File path) {
1407        final IStorageManager storageManager = IStorageManager.Stub.asInterface(
1408                ServiceManager.getService("mount"));
1409        try {
1410            final VolumeInfo[] vols = storageManager.getVolumes(0);
1411            for (VolumeInfo vol : vols) {
1412                if ((vol.getType() == VolumeInfo.TYPE_EMULATED
1413                        || vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.isMountedReadable()) {
1414                    final File internalPath = FileUtils.rewriteAfterRename(vol.getPath(),
1415                            vol.getInternalPath(), path);
1416                    if (internalPath != null && internalPath.exists()) {
1417                        return internalPath;
1418                    }
1419                }
1420            }
1421        } catch (RemoteException e) {
1422            throw e.rethrowFromSystemServer();
1423        }
1424        return path;
1425    }
1426
1427    /** {@hide} */
1428    @VisibleForTesting
1429    public @NonNull ParcelFileDescriptor openProxyFileDescriptor(
1430            int mode, ProxyFileDescriptorCallback callback, Handler handler, ThreadFactory factory)
1431                    throws IOException {
1432        Preconditions.checkNotNull(callback);
1433        MetricsLogger.count(mContext, "storage_open_proxy_file_descriptor", 1);
1434        // Retry is needed because the mount point mFuseAppLoop is using may be unmounted before
1435        // invoking StorageManagerService#openProxyFileDescriptor. In this case, we need to re-mount
1436        // the bridge by calling mountProxyFileDescriptorBridge.
1437        while (true) {
1438            try {
1439                synchronized (mFuseAppLoopLock) {
1440                    boolean newlyCreated = false;
1441                    if (mFuseAppLoop == null) {
1442                        final AppFuseMount mount = mStorageManager.mountProxyFileDescriptorBridge();
1443                        if (mount == null) {
1444                            throw new IOException("Failed to mount proxy bridge");
1445                        }
1446                        mFuseAppLoop = new FuseAppLoop(mount.mountPointId, mount.fd, factory);
1447                        newlyCreated = true;
1448                    }
1449                    if (handler == null) {
1450                        handler = new Handler(Looper.getMainLooper());
1451                    }
1452                    try {
1453                        final int fileId = mFuseAppLoop.registerCallback(callback, handler);
1454                        final ParcelFileDescriptor pfd = mStorageManager.openProxyFileDescriptor(
1455                                mFuseAppLoop.getMountPointId(), fileId, mode);
1456                        if (pfd == null) {
1457                            mFuseAppLoop.unregisterCallback(fileId);
1458                            throw new FuseUnavailableMountException(
1459                                    mFuseAppLoop.getMountPointId());
1460                        }
1461                        return pfd;
1462                    } catch (FuseUnavailableMountException exception) {
1463                        // The bridge is being unmounted. Tried to recreate it unless the bridge was
1464                        // just created.
1465                        if (newlyCreated) {
1466                            throw new IOException(exception);
1467                        }
1468                        mFuseAppLoop = null;
1469                        continue;
1470                    }
1471                }
1472            } catch (RemoteException e) {
1473                // Cannot recover from remote exception.
1474                throw new IOException(e);
1475            }
1476        }
1477    }
1478
1479    /** {@hide} */
1480    public @NonNull ParcelFileDescriptor openProxyFileDescriptor(
1481            int mode, ProxyFileDescriptorCallback callback)
1482                    throws IOException {
1483        return openProxyFileDescriptor(mode, callback, null, null);
1484    }
1485
1486    /**
1487     * Opens seekable ParcelFileDescriptor that routes file operation requests to
1488     * ProxyFileDescriptorCallback.
1489     *
1490     * @param mode The desired access mode, must be one of
1491     *     {@link ParcelFileDescriptor#MODE_READ_ONLY},
1492     *     {@link ParcelFileDescriptor#MODE_WRITE_ONLY}, or
1493     *     {@link ParcelFileDescriptor#MODE_READ_WRITE}
1494     * @param callback Callback to process file operation requests issued on returned file
1495     *     descriptor.
1496     * @param handler Handler that invokes callback methods.
1497     * @return Seekable ParcelFileDescriptor.
1498     * @throws IOException
1499     */
1500    public @NonNull ParcelFileDescriptor openProxyFileDescriptor(
1501            int mode, ProxyFileDescriptorCallback callback, Handler handler)
1502                    throws IOException {
1503        Preconditions.checkNotNull(handler);
1504        return openProxyFileDescriptor(mode, callback, handler, null);
1505    }
1506
1507
1508    /** {@hide} */
1509    @VisibleForTesting
1510    public int getProxyFileDescriptorMountPointId() {
1511        synchronized (mFuseAppLoopLock) {
1512            return mFuseAppLoop != null ? mFuseAppLoop.getMountPointId() : -1;
1513        }
1514    }
1515
1516    /**
1517     * Return quota size in bytes for all cached data belonging to the calling
1518     * app on the given storage volume.
1519     * <p>
1520     * If your app goes above this quota, your cached files will be some of the
1521     * first to be deleted when additional disk space is needed. Conversely, if
1522     * your app stays under this quota, your cached files will be some of the
1523     * last to be deleted when additional disk space is needed.
1524     * <p>
1525     * This quota will change over time depending on how frequently the user
1526     * interacts with your app, and depending on how much system-wide disk space
1527     * is used.
1528     * <p class="note">
1529     * Note: if your app uses the {@code android:sharedUserId} manifest feature,
1530     * then cached data for all packages in your shared UID is tracked together
1531     * as a single unit.
1532     * </p>
1533     *
1534     * @param storageUuid the UUID of the storage volume that you're interested
1535     *            in. The UUID for a specific path can be obtained using
1536     *            {@link #getUuidForPath(File)}.
1537     * @throws IOException when the storage device isn't present, or when it
1538     *             doesn't support cache quotas.
1539     * @see #getCacheSizeBytes(UUID)
1540     */
1541    @WorkerThread
1542    public @BytesLong long getCacheQuotaBytes(@NonNull UUID storageUuid) throws IOException {
1543        try {
1544            final ApplicationInfo app = mContext.getApplicationInfo();
1545            return mStorageManager.getCacheQuotaBytes(convert(storageUuid), app.uid);
1546        } catch (ParcelableException e) {
1547            e.maybeRethrow(IOException.class);
1548            throw new RuntimeException(e);
1549        } catch (RemoteException e) {
1550            throw e.rethrowFromSystemServer();
1551        }
1552    }
1553
1554    /** @removed */
1555    @Deprecated
1556    public long getCacheQuotaBytes(@NonNull File path) throws IOException {
1557        return getCacheQuotaBytes(getUuidForPath(path));
1558    }
1559
1560    /**
1561     * Return total size in bytes of all cached data belonging to the calling
1562     * app on the given storage volume.
1563     * <p>
1564     * Cached data tracked by this method always includes
1565     * {@link Context#getCacheDir()} and {@link Context#getCodeCacheDir()}, and
1566     * it also includes {@link Context#getExternalCacheDir()} if the primary
1567     * shared/external storage is hosted on the same storage device as your
1568     * private data.
1569     * <p class="note">
1570     * Note: if your app uses the {@code android:sharedUserId} manifest feature,
1571     * then cached data for all packages in your shared UID is tracked together
1572     * as a single unit.
1573     * </p>
1574     *
1575     * @param storageUuid the UUID of the storage volume that you're interested
1576     *            in. The UUID for a specific path can be obtained using
1577     *            {@link #getUuidForPath(File)}.
1578     * @throws IOException when the storage device isn't present, or when it
1579     *             doesn't support cache quotas.
1580     * @see #getCacheQuotaBytes(UUID)
1581     */
1582    @WorkerThread
1583    public @BytesLong long getCacheSizeBytes(@NonNull UUID storageUuid) throws IOException {
1584        try {
1585            final ApplicationInfo app = mContext.getApplicationInfo();
1586            return mStorageManager.getCacheSizeBytes(convert(storageUuid), app.uid);
1587        } catch (ParcelableException e) {
1588            e.maybeRethrow(IOException.class);
1589            throw new RuntimeException(e);
1590        } catch (RemoteException e) {
1591            throw e.rethrowFromSystemServer();
1592        }
1593    }
1594
1595    /** @removed */
1596    @Deprecated
1597    public long getCacheSizeBytes(@NonNull File path) throws IOException {
1598        return getCacheSizeBytes(getUuidForPath(path));
1599    }
1600
1601    /** @removed */
1602    @Deprecated
1603    public long getCacheQuotaBytes() throws IOException {
1604        return getCacheQuotaBytes(mContext.getCacheDir());
1605    }
1606
1607    /** @removed */
1608    @Deprecated
1609    public long getCacheSizeBytes() throws IOException {
1610        return getCacheSizeBytes(mContext.getCacheDir());
1611    }
1612
1613    /** @removed */
1614    @Deprecated
1615    public long getExternalCacheQuotaBytes() throws IOException {
1616        return getCacheQuotaBytes(mContext.getExternalCacheDir());
1617    }
1618
1619    /** @removed */
1620    @Deprecated
1621    public long getExternalCacheSizeBytes() throws IOException {
1622        return getCacheSizeBytes(mContext.getExternalCacheDir());
1623    }
1624
1625    /**
1626     * Flag indicating that a disk space allocation request should operate in an
1627     * aggressive mode. This flag should only be rarely used in situations that
1628     * are critical to system health or security.
1629     * <p>
1630     * When set, the system is more aggressive about the data that it considers
1631     * for possible deletion when allocating disk space.
1632     * <p class="note">
1633     * Note: your app must hold the
1634     * {@link android.Manifest.permission#ALLOCATE_AGGRESSIVE} permission for
1635     * this flag to take effect.
1636     * </p>
1637     *
1638     * @see #getAllocatableBytes(UUID, int)
1639     * @see #allocateBytes(UUID, long, int)
1640     * @see #allocateBytes(FileDescriptor, long, int)
1641     * @hide
1642     */
1643    @RequiresPermission(android.Manifest.permission.ALLOCATE_AGGRESSIVE)
1644    @SystemApi
1645    public static final int FLAG_ALLOCATE_AGGRESSIVE = 1 << 0;
1646
1647    /**
1648     * Flag indicating that a disk space allocation request should defy any
1649     * reserved disk space.
1650     *
1651     * @hide
1652     */
1653    public static final int FLAG_ALLOCATE_DEFY_RESERVED = 1 << 1;
1654
1655    /** @hide */
1656    @IntDef(flag = true, value = {
1657            FLAG_ALLOCATE_AGGRESSIVE,
1658            FLAG_ALLOCATE_DEFY_RESERVED,
1659    })
1660    @Retention(RetentionPolicy.SOURCE)
1661    public @interface AllocateFlags {}
1662
1663    /**
1664     * Return the maximum number of new bytes that your app can allocate for
1665     * itself on the given storage volume. This value is typically larger than
1666     * {@link File#getUsableSpace()}, since the system may be willing to delete
1667     * cached files to satisfy an allocation request. You can then allocate
1668     * space for yourself using {@link #allocateBytes(UUID, long, int)} or
1669     * {@link #allocateBytes(FileDescriptor, long, int)}.
1670     * <p>
1671     * This method is best used as a pre-flight check, such as deciding if there
1672     * is enough space to store an entire music album before you allocate space
1673     * for each audio file in the album. Attempts to allocate disk space beyond
1674     * the returned value will fail.
1675     * <p>
1676     * If the returned value is not large enough for the data you'd like to
1677     * persist, you can launch {@link #ACTION_MANAGE_STORAGE} with the
1678     * {@link #EXTRA_UUID} and {@link #EXTRA_REQUESTED_BYTES} options to help
1679     * involve the user in freeing up disk space.
1680     * <p class="note">
1681     * Note: if your app uses the {@code android:sharedUserId} manifest feature,
1682     * then allocatable space for all packages in your shared UID is tracked
1683     * together as a single unit.
1684     * </p>
1685     *
1686     * @param storageUuid the UUID of the storage volume where you're
1687     *            considering allocating disk space, since allocatable space can
1688     *            vary widely depending on the underlying storage device. The
1689     *            UUID for a specific path can be obtained using
1690     *            {@link #getUuidForPath(File)}.
1691     * @return the maximum number of new bytes that the calling app can allocate
1692     *         using {@link #allocateBytes(UUID, long, int)} or
1693     *         {@link #allocateBytes(FileDescriptor, long, int)}.
1694     * @throws IOException when the storage device isn't present, or when it
1695     *             doesn't support allocating space.
1696     */
1697    public @BytesLong long getAllocatableBytes(@NonNull UUID storageUuid)
1698            throws IOException {
1699        return getAllocatableBytes(storageUuid, 0);
1700    }
1701
1702    /** @hide */
1703    @SystemApi
1704    public long getAllocatableBytes(@NonNull UUID storageUuid, @AllocateFlags int flags)
1705            throws IOException {
1706        try {
1707            return mStorageManager.getAllocatableBytes(convert(storageUuid), flags);
1708        } catch (ParcelableException e) {
1709            e.maybeRethrow(IOException.class);
1710            throw new RuntimeException(e);
1711        } catch (RemoteException e) {
1712            throw e.rethrowFromSystemServer();
1713        }
1714    }
1715
1716    /** @removed */
1717    @Deprecated
1718    public long getAllocatableBytes(@NonNull File path, @AllocateFlags int flags)
1719            throws IOException {
1720        return getAllocatableBytes(getUuidForPath(path), flags);
1721    }
1722
1723    /**
1724     * Allocate the requested number of bytes for your application to use on the
1725     * given storage volume. This will cause the system to delete any cached
1726     * files necessary to satisfy your request.
1727     * <p>
1728     * Attempts to allocate disk space beyond the value returned by
1729     * {@link #getAllocatableBytes(UUID, int)} will fail.
1730     * <p>
1731     * Since multiple apps can be running simultaneously, this method may be
1732     * subject to race conditions. If possible, consider using
1733     * {@link #allocateBytes(FileDescriptor, long, int)} which will guarantee
1734     * that bytes are allocated to an opened file.
1735     *
1736     * @param storageUuid the UUID of the storage volume where you'd like to
1737     *            allocate disk space. The UUID for a specific path can be
1738     *            obtained using {@link #getUuidForPath(File)}.
1739     * @param bytes the number of bytes to allocate.
1740     * @throws IOException when the storage device isn't present, or when it
1741     *             doesn't support allocating space, or if the device had
1742     *             trouble allocating the requested space.
1743     * @see #getAllocatableBytes(UUID, int)
1744     */
1745    public void allocateBytes(@NonNull UUID storageUuid, @BytesLong long bytes)
1746            throws IOException {
1747        allocateBytes(storageUuid, bytes, 0);
1748    }
1749
1750    /** @hide */
1751    @SystemApi
1752    public void allocateBytes(@NonNull UUID storageUuid, @BytesLong long bytes,
1753            @AllocateFlags int flags) throws IOException {
1754        try {
1755            mStorageManager.allocateBytes(convert(storageUuid), bytes, flags);
1756        } catch (ParcelableException e) {
1757            e.maybeRethrow(IOException.class);
1758        } catch (RemoteException e) {
1759            throw e.rethrowFromSystemServer();
1760        }
1761    }
1762
1763    /** @removed */
1764    @Deprecated
1765    public void allocateBytes(@NonNull File path, @BytesLong long bytes, @AllocateFlags int flags)
1766            throws IOException {
1767        allocateBytes(getUuidForPath(path), bytes, flags);
1768    }
1769
1770    /**
1771     * Allocate the requested number of bytes for your application to use in the
1772     * given open file. This will cause the system to delete any cached files
1773     * necessary to satisfy your request.
1774     * <p>
1775     * Attempts to allocate disk space beyond the value returned by
1776     * {@link #getAllocatableBytes(UUID, int)} will fail.
1777     * <p>
1778     * This method guarantees that bytes have been allocated to the opened file,
1779     * otherwise it will throw if fast allocation is not possible. Fast
1780     * allocation is typically only supported in private app data directories,
1781     * and on shared/external storage devices which are emulated.
1782     *
1783     * @param fd the open file that you'd like to allocate disk space for.
1784     * @param bytes the number of bytes to allocate. This is the desired final
1785     *            size of the open file. If the open file is smaller than this
1786     *            requested size, it will be extended without modifying any
1787     *            existing contents. If the open file is larger than this
1788     *            requested size, it will be truncated.
1789     * @throws IOException when the storage device isn't present, or when it
1790     *             doesn't support allocating space, or if the device had
1791     *             trouble allocating the requested space.
1792     * @see #getAllocatableBytes(UUID, int)
1793     * @see Environment#isExternalStorageEmulated(File)
1794     */
1795    public void allocateBytes(FileDescriptor fd, @BytesLong long bytes) throws IOException {
1796        allocateBytes(fd, bytes, 0);
1797    }
1798
1799    /** @hide */
1800    @SystemApi
1801    public void allocateBytes(FileDescriptor fd, @BytesLong long bytes, @AllocateFlags int flags)
1802            throws IOException {
1803        final File file = ParcelFileDescriptor.getFile(fd);
1804        for (int i = 0; i < 3; i++) {
1805            try {
1806                final long haveBytes = Os.fstat(fd).st_blocks * 512;
1807                final long needBytes = bytes - haveBytes;
1808
1809                if (needBytes > 0) {
1810                    allocateBytes(file, needBytes, flags);
1811                }
1812
1813                Os.posix_fallocate(fd, 0, bytes);
1814                return;
1815            } catch (ErrnoException e) {
1816                if (e.errno == OsConstants.ENOSPC) {
1817                    Log.w(TAG, "Odd, not enough space; let's try again?");
1818                    continue;
1819                }
1820                throw e.rethrowAsIOException();
1821            }
1822        }
1823        throw new IOException(
1824                "Well this is embarassing; we can't allocate " + bytes + " for " + file);
1825    }
1826
1827    private static final String XATTR_CACHE_GROUP = "user.cache_group";
1828    private static final String XATTR_CACHE_TOMBSTONE = "user.cache_tombstone";
1829
1830    /** {@hide} */
1831    private static void setCacheBehavior(File path, String name, boolean enabled)
1832            throws IOException {
1833        if (!path.isDirectory()) {
1834            throw new IOException("Cache behavior can only be set on directories");
1835        }
1836        if (enabled) {
1837            try {
1838                Os.setxattr(path.getAbsolutePath(), name,
1839                        "1".getBytes(StandardCharsets.UTF_8), 0);
1840            } catch (ErrnoException e) {
1841                throw e.rethrowAsIOException();
1842            }
1843        } else {
1844            try {
1845                Os.removexattr(path.getAbsolutePath(), name);
1846            } catch (ErrnoException e) {
1847                if (e.errno != OsConstants.ENODATA) {
1848                    throw e.rethrowAsIOException();
1849                }
1850            }
1851        }
1852    }
1853
1854    /** {@hide} */
1855    private static boolean isCacheBehavior(File path, String name) throws IOException {
1856        try {
1857            Os.getxattr(path.getAbsolutePath(), name);
1858            return true;
1859        } catch (ErrnoException e) {
1860            if (e.errno != OsConstants.ENODATA) {
1861                throw e.rethrowAsIOException();
1862            } else {
1863                return false;
1864            }
1865        }
1866    }
1867
1868    /**
1869     * Enable or disable special cache behavior that treats this directory and
1870     * its contents as an entire group.
1871     * <p>
1872     * When enabled and this directory is considered for automatic deletion by
1873     * the OS, all contained files will either be deleted together, or not at
1874     * all. This is useful when you have a directory that contains several
1875     * related metadata files that depend on each other, such as movie file and
1876     * a subtitle file.
1877     * <p>
1878     * When enabled, the <em>newest</em> {@link File#lastModified()} value of
1879     * any contained files is considered the modified time of the entire
1880     * directory.
1881     * <p>
1882     * This behavior can only be set on a directory, and it applies recursively
1883     * to all contained files and directories.
1884     */
1885    public void setCacheBehaviorGroup(File path, boolean group) throws IOException {
1886        setCacheBehavior(path, XATTR_CACHE_GROUP, group);
1887    }
1888
1889    /**
1890     * Read the current value set by
1891     * {@link #setCacheBehaviorGroup(File, boolean)}.
1892     */
1893    public boolean isCacheBehaviorGroup(File path) throws IOException {
1894        return isCacheBehavior(path, XATTR_CACHE_GROUP);
1895    }
1896
1897    /** @removed */
1898    @Deprecated
1899    public void setCacheBehaviorAtomic(File path, boolean atomic) throws IOException {
1900        setCacheBehaviorGroup(path, atomic);
1901    }
1902
1903    /** @removed */
1904    @Deprecated
1905    public boolean isCacheBehaviorAtomic(File path) throws IOException {
1906        return isCacheBehaviorGroup(path);
1907    }
1908
1909    /**
1910     * Enable or disable special cache behavior that leaves deleted cache files
1911     * intact as tombstones.
1912     * <p>
1913     * When enabled and a file contained in this directory is automatically
1914     * deleted by the OS, the file will be truncated to have a length of 0 bytes
1915     * instead of being fully deleted. This is useful if you need to distinguish
1916     * between a file that was deleted versus one that never existed.
1917     * <p>
1918     * This behavior can only be set on a directory, and it applies recursively
1919     * to all contained files and directories.
1920     * <p class="note">
1921     * Note: this behavior is ignored completely if the user explicitly requests
1922     * that all cached data be cleared.
1923     * </p>
1924     */
1925    public void setCacheBehaviorTombstone(File path, boolean tombstone) throws IOException {
1926        setCacheBehavior(path, XATTR_CACHE_TOMBSTONE, tombstone);
1927    }
1928
1929    /**
1930     * Read the current value set by
1931     * {@link #setCacheBehaviorTombstone(File, boolean)}.
1932     */
1933    public boolean isCacheBehaviorTombstone(File path) throws IOException {
1934        return isCacheBehavior(path, XATTR_CACHE_TOMBSTONE);
1935    }
1936
1937    /** {@hide} */
1938    public static UUID convert(String uuid) {
1939        if (Objects.equals(uuid, UUID_PRIVATE_INTERNAL)) {
1940            return UUID_DEFAULT;
1941        } else if (Objects.equals(uuid, UUID_PRIMARY_PHYSICAL)) {
1942            return UUID_PRIMARY_PHYSICAL_;
1943        } else if (Objects.equals(uuid, UUID_SYSTEM)) {
1944            return UUID_SYSTEM_;
1945        } else {
1946            return UUID.fromString(uuid);
1947        }
1948    }
1949
1950    /** {@hide} */
1951    public static String convert(UUID storageUuid) {
1952        if (UUID_DEFAULT.equals(storageUuid)) {
1953            return UUID_PRIVATE_INTERNAL;
1954        } else if (UUID_PRIMARY_PHYSICAL_.equals(storageUuid)) {
1955            return UUID_PRIMARY_PHYSICAL;
1956        } else if (UUID_SYSTEM_.equals(storageUuid)) {
1957            return UUID_SYSTEM;
1958        } else {
1959            return storageUuid.toString();
1960        }
1961    }
1962
1963    private final Object mFuseAppLoopLock = new Object();
1964
1965    @GuardedBy("mFuseAppLoopLock")
1966    private @Nullable FuseAppLoop mFuseAppLoop = null;
1967
1968    /// Consts to match the password types in cryptfs.h
1969    /** @hide */
1970    public static final int CRYPT_TYPE_PASSWORD = 0;
1971    /** @hide */
1972    public static final int CRYPT_TYPE_DEFAULT = 1;
1973    /** @hide */
1974    public static final int CRYPT_TYPE_PATTERN = 2;
1975    /** @hide */
1976    public static final int CRYPT_TYPE_PIN = 3;
1977
1978    // Constants for the data available via StorageManagerService.getField.
1979    /** @hide */
1980    public static final String SYSTEM_LOCALE_KEY = "SystemLocale";
1981    /** @hide */
1982    public static final String OWNER_INFO_KEY = "OwnerInfo";
1983    /** @hide */
1984    public static final String PATTERN_VISIBLE_KEY = "PatternVisible";
1985    /** @hide */
1986    public static final String PASSWORD_VISIBLE_KEY = "PasswordVisible";
1987}
1988