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