Loading core/java/android/hardware/biometrics/SensorProperties.java +46 −41 Original line number Diff line number Diff line Loading @@ -17,66 +17,71 @@ package android.hardware.biometrics; import android.annotation.IntDef; import android.os.Parcel; import android.os.Parcelable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * The base class containing all sensor-agnostic information. This is a superset of the * {@link android.hardware.biometrics.common.CommonProps}, and provides backwards-compatible * behavior with the older generation of HIDL (non-AIDL) interfaces. * The base class containing all modality-agnostic information. * @hide */ public class SensorProperties { /** * A sensor that meets the requirements for Class 1 biometrics as defined in the CDD. This does * not correspond to a public BiometricManager.Authenticators constant. Sensors of this strength * are not available to applications via the public API surface. * @hide */ public class SensorProperties implements Parcelable { public static final int STRENGTH_CONVENIENCE = 0; /** * A sensor that meets the requirements for Class 2 biometrics as defined in the CDD. * Corresponds to BiometricManager.Authenticators.BIOMETRIC_WEAK. * @hide */ public static final int STRENGTH_WEAK = 1; /** * A sensor that meets the requirements for Class 3 biometrics as defined in the CDD. * Corresponds to BiometricManager.Authenticators.BIOMETRIC_STRONG. * * Notably, this is the only strength that allows generation of HardwareAuthToken(s). * @hide */ public static final int STRENGTH_STRONG = 2; /** * @hide */ @IntDef({STRENGTH_CONVENIENCE, STRENGTH_WEAK, STRENGTH_STRONG}) @Retention(RetentionPolicy.SOURCE) public @interface Strength {} public final int sensorId; @Strength public final int sensorStrength; public final int maxEnrollmentsPerUser; private final int mSensorId; @Strength private final int mSensorStrength; protected SensorProperties(int sensorId, @Strength int sensorStrength, int maxEnrollmentsPerUser) { this.sensorId = sensorId; this.sensorStrength = sensorStrength; this.maxEnrollmentsPerUser = maxEnrollmentsPerUser; } protected SensorProperties(Parcel in) { sensorId = in.readInt(); sensorStrength = in.readInt(); maxEnrollmentsPerUser = in.readInt(); } public static final Creator<SensorProperties> CREATOR = new Creator<SensorProperties>() { @Override public SensorProperties createFromParcel(Parcel in) { return new SensorProperties(in); } @Override public SensorProperties[] newArray(int size) { return new SensorProperties[size]; /** * @hide */ public SensorProperties(int sensorId, @Strength int sensorStrength) { mSensorId = sensorId; mSensorStrength = sensorStrength; } }; @Override public int describeContents() { return 0; /** * @return The sensor's unique identifier. * @hide */ public int getSensorId() { return mSensorId; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(sensorId); dest.writeInt(sensorStrength); dest.writeInt(maxEnrollmentsPerUser); /** * @return The sensor's strength. * @hide */ @Strength public int getSensorStrength() { return mSensorStrength; } } core/java/android/hardware/biometrics/SensorPropertiesInternal.java 0 → 100644 +75 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.hardware.biometrics; import android.annotation.IntDef; import android.os.Parcel; import android.os.Parcelable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * The base class containing all modality-agnostic information. This is a superset of the * {@link android.hardware.biometrics.common.CommonProps}, and provides backwards-compatible * behavior with the older generation of HIDL (non-AIDL) interfaces. * @hide */ public class SensorPropertiesInternal implements Parcelable { public final int sensorId; @SensorProperties.Strength public final int sensorStrength; public final int maxEnrollmentsPerUser; protected SensorPropertiesInternal(int sensorId, @SensorProperties.Strength int sensorStrength, int maxEnrollmentsPerUser) { this.sensorId = sensorId; this.sensorStrength = sensorStrength; this.maxEnrollmentsPerUser = maxEnrollmentsPerUser; } protected SensorPropertiesInternal(Parcel in) { sensorId = in.readInt(); sensorStrength = in.readInt(); maxEnrollmentsPerUser = in.readInt(); } public static final Creator<SensorPropertiesInternal> CREATOR = new Creator<SensorPropertiesInternal>() { @Override public SensorPropertiesInternal createFromParcel(Parcel in) { return new SensorPropertiesInternal(in); } @Override public SensorPropertiesInternal[] newArray(int size) { return new SensorPropertiesInternal[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(sensorId); dest.writeInt(sensorStrength); dest.writeInt(maxEnrollmentsPerUser); } } core/java/android/hardware/face/FaceManager.java +26 −7 Original line number Diff line number Diff line Loading @@ -49,9 +49,6 @@ import com.android.internal.os.SomeArgs; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; /** * A class that coordinates access to the face authentication hardware. Loading Loading @@ -299,6 +296,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan * int[], Surface)} with {@code surface} set to null. * * @see FaceManager#enroll(int, byte[], CancellationSignal, EnrollmentCallback, int[], Surface) * @hide */ @RequiresPermission(MANAGE_BIOMETRIC) public void enroll(int userId, byte[] hardwareAuthToken, CancellationSignal cancel, Loading Loading @@ -443,7 +441,8 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan */ @RequiresPermission(MANAGE_BIOMETRIC) public void generateChallenge(GenerateChallengeCallback callback) { final List<FaceSensorProperties> faceSensorProperties = getSensorProperties(); final List<FaceSensorPropertiesInternal> faceSensorProperties = getSensorPropertiesInternal(); if (faceSensorProperties.isEmpty()) { Slog.e(TAG, "No sensors"); return; Loading @@ -460,7 +459,8 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan */ @RequiresPermission(MANAGE_BIOMETRIC) public void revokeChallenge() { final List<FaceSensorProperties> faceSensorProperties = getSensorProperties(); final List<FaceSensorPropertiesInternal> faceSensorProperties = getSensorPropertiesInternal(); if (faceSensorProperties.isEmpty()) { Slog.e(TAG, "No sensors during revokeChallenge"); } Loading Loading @@ -597,6 +597,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan * Determine if there is a face enrolled. * * @return true if a face is enrolled, false otherwise * @hide */ @RequiresPermission(USE_BIOMETRIC_INTERNAL) public boolean hasEnrolledTemplates() { Loading Loading @@ -632,6 +633,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan * Determine if face authentication sensor hardware is present and functional. * * @return true if hardware is present and functional, false otherwise. * @hide */ @RequiresPermission(USE_BIOMETRIC_INTERNAL) public boolean isHardwareDetected() { Loading @@ -647,18 +649,33 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan return false; } /** * Retrieves a list of properties for all face authentication sensors on the device. * @hide */ @NonNull public List<FaceSensorProperties> getSensorProperties() { final List<FaceSensorProperties> properties = new ArrayList<>(); final List<FaceSensorPropertiesInternal> internalProperties = getSensorPropertiesInternal(); for (FaceSensorPropertiesInternal internalProp : internalProperties) { properties.add(FaceSensorProperties.from(internalProp)); } return properties; } /** * Get statically configured sensor properties. * @hide */ @RequiresPermission(USE_BIOMETRIC_INTERNAL) @NonNull public List<FaceSensorProperties> getSensorProperties() { public List<FaceSensorPropertiesInternal> getSensorPropertiesInternal() { try { if (mService == null || !mService.isHardwareDetected(mContext.getOpPackageName())) { return new ArrayList<>(); } return mService.getSensorProperties(mContext.getOpPackageName()); return mService.getSensorPropertiesInternal(mContext.getOpPackageName()); } catch (RemoteException e) { e.rethrowFromSystemServer(); } Loading Loading @@ -874,6 +891,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan /** * Container for callback data from {@link FaceManager#authenticate(CryptoObject, * CancellationSignal, int, AuthenticationCallback, Handler)}. * @hide */ public static class AuthenticationResult { private Face mFace; Loading Loading @@ -943,6 +961,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan * FaceManager#authenticate(CryptoObject, CancellationSignal, * int, AuthenticationCallback, Handler) } must provide an implementation of this for listening * to face events. * @hide */ public abstract static class AuthenticationCallback extends BiometricAuthenticator.AuthenticationCallback { Loading core/java/android/hardware/face/FaceSensorProperties.java +9 −60 Original line number Diff line number Diff line Loading @@ -16,76 +16,25 @@ package android.hardware.face; import android.os.Parcel; import android.os.Parcelable; import android.hardware.biometrics.SensorProperties; /** * Container for face sensor properties. * @hide */ public class FaceSensorProperties implements Parcelable { public class FaceSensorProperties extends SensorProperties { /** * A statically configured ID representing this sensor. Sensor IDs must be unique across all * biometrics across the device, starting at 0, and in increments of 1. */ public final int sensorId; /** * True if the sensor is able to perform generic face detection, without running the * matching algorithm, and without affecting the lockout counter. */ public final boolean supportsFaceDetection; /** * True if the sensor is able to provide self illumination in dark scenarios, without support * from above the HAL. */ public final boolean supportsSelfIllumination; /** * Maximum number of enrollments a user/profile can have. * @hide */ public final int maxTemplatesAllowed; public static FaceSensorProperties from(FaceSensorPropertiesInternal internalProp) { return new FaceSensorProperties(internalProp.sensorId, internalProp.sensorStrength); } /** * Initializes SensorProperties with specified values * @hide */ public FaceSensorProperties(int sensorId, boolean supportsFaceDetection, boolean supportsSelfIllumination, int maxTemplatesAllowed) { this.sensorId = sensorId; this.supportsFaceDetection = supportsFaceDetection; this.supportsSelfIllumination = supportsSelfIllumination; this.maxTemplatesAllowed = maxTemplatesAllowed; public FaceSensorProperties(int sensorId, int sensorStrength) { super(sensorId, sensorStrength); } protected FaceSensorProperties(Parcel in) { sensorId = in.readInt(); supportsFaceDetection = in.readBoolean(); supportsSelfIllumination = in.readBoolean(); maxTemplatesAllowed = in.readInt(); } public static final Creator<FaceSensorProperties> CREATOR = new Creator<FaceSensorProperties>() { @Override public FaceSensorProperties createFromParcel(Parcel in) { return new FaceSensorProperties(in); } @Override public FaceSensorProperties[] newArray(int size) { return new FaceSensorProperties[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(sensorId); dest.writeBoolean(supportsFaceDetection); dest.writeBoolean(supportsSelfIllumination); dest.writeInt(maxTemplatesAllowed); } } core/java/android/hardware/face/FaceSensorProperties.aidl→core/java/android/hardware/face/FaceSensorPropertiesInternal.aidl +1 −1 Original line number Diff line number Diff line Loading @@ -15,4 +15,4 @@ */ package android.hardware.face; parcelable FaceSensorProperties; No newline at end of file parcelable FaceSensorPropertiesInternal; No newline at end of file Loading
core/java/android/hardware/biometrics/SensorProperties.java +46 −41 Original line number Diff line number Diff line Loading @@ -17,66 +17,71 @@ package android.hardware.biometrics; import android.annotation.IntDef; import android.os.Parcel; import android.os.Parcelable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * The base class containing all sensor-agnostic information. This is a superset of the * {@link android.hardware.biometrics.common.CommonProps}, and provides backwards-compatible * behavior with the older generation of HIDL (non-AIDL) interfaces. * The base class containing all modality-agnostic information. * @hide */ public class SensorProperties { /** * A sensor that meets the requirements for Class 1 biometrics as defined in the CDD. This does * not correspond to a public BiometricManager.Authenticators constant. Sensors of this strength * are not available to applications via the public API surface. * @hide */ public class SensorProperties implements Parcelable { public static final int STRENGTH_CONVENIENCE = 0; /** * A sensor that meets the requirements for Class 2 biometrics as defined in the CDD. * Corresponds to BiometricManager.Authenticators.BIOMETRIC_WEAK. * @hide */ public static final int STRENGTH_WEAK = 1; /** * A sensor that meets the requirements for Class 3 biometrics as defined in the CDD. * Corresponds to BiometricManager.Authenticators.BIOMETRIC_STRONG. * * Notably, this is the only strength that allows generation of HardwareAuthToken(s). * @hide */ public static final int STRENGTH_STRONG = 2; /** * @hide */ @IntDef({STRENGTH_CONVENIENCE, STRENGTH_WEAK, STRENGTH_STRONG}) @Retention(RetentionPolicy.SOURCE) public @interface Strength {} public final int sensorId; @Strength public final int sensorStrength; public final int maxEnrollmentsPerUser; private final int mSensorId; @Strength private final int mSensorStrength; protected SensorProperties(int sensorId, @Strength int sensorStrength, int maxEnrollmentsPerUser) { this.sensorId = sensorId; this.sensorStrength = sensorStrength; this.maxEnrollmentsPerUser = maxEnrollmentsPerUser; } protected SensorProperties(Parcel in) { sensorId = in.readInt(); sensorStrength = in.readInt(); maxEnrollmentsPerUser = in.readInt(); } public static final Creator<SensorProperties> CREATOR = new Creator<SensorProperties>() { @Override public SensorProperties createFromParcel(Parcel in) { return new SensorProperties(in); } @Override public SensorProperties[] newArray(int size) { return new SensorProperties[size]; /** * @hide */ public SensorProperties(int sensorId, @Strength int sensorStrength) { mSensorId = sensorId; mSensorStrength = sensorStrength; } }; @Override public int describeContents() { return 0; /** * @return The sensor's unique identifier. * @hide */ public int getSensorId() { return mSensorId; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(sensorId); dest.writeInt(sensorStrength); dest.writeInt(maxEnrollmentsPerUser); /** * @return The sensor's strength. * @hide */ @Strength public int getSensorStrength() { return mSensorStrength; } }
core/java/android/hardware/biometrics/SensorPropertiesInternal.java 0 → 100644 +75 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.hardware.biometrics; import android.annotation.IntDef; import android.os.Parcel; import android.os.Parcelable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * The base class containing all modality-agnostic information. This is a superset of the * {@link android.hardware.biometrics.common.CommonProps}, and provides backwards-compatible * behavior with the older generation of HIDL (non-AIDL) interfaces. * @hide */ public class SensorPropertiesInternal implements Parcelable { public final int sensorId; @SensorProperties.Strength public final int sensorStrength; public final int maxEnrollmentsPerUser; protected SensorPropertiesInternal(int sensorId, @SensorProperties.Strength int sensorStrength, int maxEnrollmentsPerUser) { this.sensorId = sensorId; this.sensorStrength = sensorStrength; this.maxEnrollmentsPerUser = maxEnrollmentsPerUser; } protected SensorPropertiesInternal(Parcel in) { sensorId = in.readInt(); sensorStrength = in.readInt(); maxEnrollmentsPerUser = in.readInt(); } public static final Creator<SensorPropertiesInternal> CREATOR = new Creator<SensorPropertiesInternal>() { @Override public SensorPropertiesInternal createFromParcel(Parcel in) { return new SensorPropertiesInternal(in); } @Override public SensorPropertiesInternal[] newArray(int size) { return new SensorPropertiesInternal[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(sensorId); dest.writeInt(sensorStrength); dest.writeInt(maxEnrollmentsPerUser); } }
core/java/android/hardware/face/FaceManager.java +26 −7 Original line number Diff line number Diff line Loading @@ -49,9 +49,6 @@ import com.android.internal.os.SomeArgs; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; /** * A class that coordinates access to the face authentication hardware. Loading Loading @@ -299,6 +296,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan * int[], Surface)} with {@code surface} set to null. * * @see FaceManager#enroll(int, byte[], CancellationSignal, EnrollmentCallback, int[], Surface) * @hide */ @RequiresPermission(MANAGE_BIOMETRIC) public void enroll(int userId, byte[] hardwareAuthToken, CancellationSignal cancel, Loading Loading @@ -443,7 +441,8 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan */ @RequiresPermission(MANAGE_BIOMETRIC) public void generateChallenge(GenerateChallengeCallback callback) { final List<FaceSensorProperties> faceSensorProperties = getSensorProperties(); final List<FaceSensorPropertiesInternal> faceSensorProperties = getSensorPropertiesInternal(); if (faceSensorProperties.isEmpty()) { Slog.e(TAG, "No sensors"); return; Loading @@ -460,7 +459,8 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan */ @RequiresPermission(MANAGE_BIOMETRIC) public void revokeChallenge() { final List<FaceSensorProperties> faceSensorProperties = getSensorProperties(); final List<FaceSensorPropertiesInternal> faceSensorProperties = getSensorPropertiesInternal(); if (faceSensorProperties.isEmpty()) { Slog.e(TAG, "No sensors during revokeChallenge"); } Loading Loading @@ -597,6 +597,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan * Determine if there is a face enrolled. * * @return true if a face is enrolled, false otherwise * @hide */ @RequiresPermission(USE_BIOMETRIC_INTERNAL) public boolean hasEnrolledTemplates() { Loading Loading @@ -632,6 +633,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan * Determine if face authentication sensor hardware is present and functional. * * @return true if hardware is present and functional, false otherwise. * @hide */ @RequiresPermission(USE_BIOMETRIC_INTERNAL) public boolean isHardwareDetected() { Loading @@ -647,18 +649,33 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan return false; } /** * Retrieves a list of properties for all face authentication sensors on the device. * @hide */ @NonNull public List<FaceSensorProperties> getSensorProperties() { final List<FaceSensorProperties> properties = new ArrayList<>(); final List<FaceSensorPropertiesInternal> internalProperties = getSensorPropertiesInternal(); for (FaceSensorPropertiesInternal internalProp : internalProperties) { properties.add(FaceSensorProperties.from(internalProp)); } return properties; } /** * Get statically configured sensor properties. * @hide */ @RequiresPermission(USE_BIOMETRIC_INTERNAL) @NonNull public List<FaceSensorProperties> getSensorProperties() { public List<FaceSensorPropertiesInternal> getSensorPropertiesInternal() { try { if (mService == null || !mService.isHardwareDetected(mContext.getOpPackageName())) { return new ArrayList<>(); } return mService.getSensorProperties(mContext.getOpPackageName()); return mService.getSensorPropertiesInternal(mContext.getOpPackageName()); } catch (RemoteException e) { e.rethrowFromSystemServer(); } Loading Loading @@ -874,6 +891,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan /** * Container for callback data from {@link FaceManager#authenticate(CryptoObject, * CancellationSignal, int, AuthenticationCallback, Handler)}. * @hide */ public static class AuthenticationResult { private Face mFace; Loading Loading @@ -943,6 +961,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan * FaceManager#authenticate(CryptoObject, CancellationSignal, * int, AuthenticationCallback, Handler) } must provide an implementation of this for listening * to face events. * @hide */ public abstract static class AuthenticationCallback extends BiometricAuthenticator.AuthenticationCallback { Loading
core/java/android/hardware/face/FaceSensorProperties.java +9 −60 Original line number Diff line number Diff line Loading @@ -16,76 +16,25 @@ package android.hardware.face; import android.os.Parcel; import android.os.Parcelable; import android.hardware.biometrics.SensorProperties; /** * Container for face sensor properties. * @hide */ public class FaceSensorProperties implements Parcelable { public class FaceSensorProperties extends SensorProperties { /** * A statically configured ID representing this sensor. Sensor IDs must be unique across all * biometrics across the device, starting at 0, and in increments of 1. */ public final int sensorId; /** * True if the sensor is able to perform generic face detection, without running the * matching algorithm, and without affecting the lockout counter. */ public final boolean supportsFaceDetection; /** * True if the sensor is able to provide self illumination in dark scenarios, without support * from above the HAL. */ public final boolean supportsSelfIllumination; /** * Maximum number of enrollments a user/profile can have. * @hide */ public final int maxTemplatesAllowed; public static FaceSensorProperties from(FaceSensorPropertiesInternal internalProp) { return new FaceSensorProperties(internalProp.sensorId, internalProp.sensorStrength); } /** * Initializes SensorProperties with specified values * @hide */ public FaceSensorProperties(int sensorId, boolean supportsFaceDetection, boolean supportsSelfIllumination, int maxTemplatesAllowed) { this.sensorId = sensorId; this.supportsFaceDetection = supportsFaceDetection; this.supportsSelfIllumination = supportsSelfIllumination; this.maxTemplatesAllowed = maxTemplatesAllowed; public FaceSensorProperties(int sensorId, int sensorStrength) { super(sensorId, sensorStrength); } protected FaceSensorProperties(Parcel in) { sensorId = in.readInt(); supportsFaceDetection = in.readBoolean(); supportsSelfIllumination = in.readBoolean(); maxTemplatesAllowed = in.readInt(); } public static final Creator<FaceSensorProperties> CREATOR = new Creator<FaceSensorProperties>() { @Override public FaceSensorProperties createFromParcel(Parcel in) { return new FaceSensorProperties(in); } @Override public FaceSensorProperties[] newArray(int size) { return new FaceSensorProperties[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(sensorId); dest.writeBoolean(supportsFaceDetection); dest.writeBoolean(supportsSelfIllumination); dest.writeInt(maxTemplatesAllowed); } }
core/java/android/hardware/face/FaceSensorProperties.aidl→core/java/android/hardware/face/FaceSensorPropertiesInternal.aidl +1 −1 Original line number Diff line number Diff line Loading @@ -15,4 +15,4 @@ */ package android.hardware.face; parcelable FaceSensorProperties; No newline at end of file parcelable FaceSensorPropertiesInternal; No newline at end of file