Updates to support minSdkVersion=21

This creates a new "MasterKey" class that holds onto a key alias even
when that key may not exist in Android keystore.

Methods in EncryptedFile and EncryptedSharedPreferences also accept a
new MasterKey instance, in addition to the String "masterKeyAlias".

Relnote: "Add support for Android Lollipop (API 21+) via a new MasterKey
class. Because the Android keystore is not used prior to Android M (API
23+), developers should be aware that the keystore will _not_ be used
on Android L (API 21 and 22)."

Test: Existing and new tests pass
Bug: 132325342
Change-Id: I7c12d205273e4b652271865e53ff6c406632f407
diff --git a/security/crypto/api/1.1.0-alpha01.txt b/security/crypto/api/1.1.0-alpha01.txt
index 3fb36a8..b6fa8f0 100644
--- a/security/crypto/api/1.1.0-alpha01.txt
+++ b/security/crypto/api/1.1.0-alpha01.txt
@@ -7,7 +7,8 @@
   }
 
   public static final class EncryptedFile.Builder {
-    ctor public EncryptedFile.Builder(java.io.File, android.content.Context, String, androidx.security.crypto.EncryptedFile.FileEncryptionScheme);
+    ctor @Deprecated public EncryptedFile.Builder(java.io.File, android.content.Context, String, androidx.security.crypto.EncryptedFile.FileEncryptionScheme);
+    ctor public EncryptedFile.Builder(android.content.Context, java.io.File, androidx.security.crypto.MasterKey, androidx.security.crypto.EncryptedFile.FileEncryptionScheme);
     method public androidx.security.crypto.EncryptedFile build() throws java.security.GeneralSecurityException, java.io.IOException;
     method public androidx.security.crypto.EncryptedFile.Builder setKeysetAlias(String);
     method public androidx.security.crypto.EncryptedFile.Builder setKeysetPrefName(String);
@@ -19,7 +20,8 @@
 
   public final class EncryptedSharedPreferences implements android.content.SharedPreferences {
     method public boolean contains(String?);
-    method public static android.content.SharedPreferences create(String, String, android.content.Context, androidx.security.crypto.EncryptedSharedPreferences.PrefKeyEncryptionScheme, androidx.security.crypto.EncryptedSharedPreferences.PrefValueEncryptionScheme) throws java.security.GeneralSecurityException, java.io.IOException;
+    method public static android.content.SharedPreferences create(android.content.Context, String, androidx.security.crypto.MasterKey, androidx.security.crypto.EncryptedSharedPreferences.PrefKeyEncryptionScheme, androidx.security.crypto.EncryptedSharedPreferences.PrefValueEncryptionScheme) throws java.security.GeneralSecurityException, java.io.IOException;
+    method @Deprecated public static android.content.SharedPreferences create(String, String, android.content.Context, androidx.security.crypto.EncryptedSharedPreferences.PrefKeyEncryptionScheme, androidx.security.crypto.EncryptedSharedPreferences.PrefValueEncryptionScheme) throws java.security.GeneralSecurityException, java.io.IOException;
     method public android.content.SharedPreferences.Editor edit();
     method public java.util.Map<java.lang.String!,?> getAll();
     method public boolean getBoolean(String?, boolean);
@@ -40,9 +42,34 @@
     enum_constant public static final androidx.security.crypto.EncryptedSharedPreferences.PrefValueEncryptionScheme AES256_GCM;
   }
 
-  public final class MasterKeys {
-    method public static String getOrCreate(android.security.keystore.KeyGenParameterSpec) throws java.security.GeneralSecurityException, java.io.IOException;
-    field public static final android.security.keystore.KeyGenParameterSpec AES256_GCM_SPEC;
+  public final class MasterKey {
+    method public static int getDefaultAuthenticationValidityDurationSeconds();
+    method public int getUserAuthenticationValidityDurationSeconds();
+    method public boolean isKeyStoreBacked();
+    method public boolean isStrongBoxBacked();
+    method public boolean isUserAuthenticationRequired();
+    field public static final int DEFAULT_AES_GCM_MASTER_KEY_SIZE = 256; // 0x100
+    field public static final String DEFAULT_MASTER_KEY_ALIAS = "_androidx_security_master_key_";
+  }
+
+  public static final class MasterKey.Builder {
+    ctor public MasterKey.Builder(android.content.Context);
+    ctor public MasterKey.Builder(android.content.Context, String);
+    method public androidx.security.crypto.MasterKey build() throws java.security.GeneralSecurityException, java.io.IOException;
+    method @RequiresApi(android.os.Build.VERSION_CODES.M) public androidx.security.crypto.MasterKey.Builder setKeyGenParameterSpec(android.security.keystore.KeyGenParameterSpec);
+    method public androidx.security.crypto.MasterKey.Builder setKeyScheme(androidx.security.crypto.MasterKey.KeyScheme);
+    method public androidx.security.crypto.MasterKey.Builder setTryToUseStrongBox(boolean);
+    method public androidx.security.crypto.MasterKey.Builder setUserAuthenticationRequired(boolean);
+    method public androidx.security.crypto.MasterKey.Builder setUserAuthenticationRequired(boolean, @IntRange(from=1) int);
+  }
+
+  public enum MasterKey.KeyScheme {
+    enum_constant public static final androidx.security.crypto.MasterKey.KeyScheme AES256_GCM;
+  }
+
+  @Deprecated public final class MasterKeys {
+    method @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.M) public static String getOrCreate(android.security.keystore.KeyGenParameterSpec) throws java.security.GeneralSecurityException, java.io.IOException;
+    field @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.M) public static final android.security.keystore.KeyGenParameterSpec AES256_GCM_SPEC;
   }
 
 }
diff --git a/security/crypto/api/current.txt b/security/crypto/api/current.txt
index 3fb36a8..b6fa8f0 100644
--- a/security/crypto/api/current.txt
+++ b/security/crypto/api/current.txt
@@ -7,7 +7,8 @@
   }
 
   public static final class EncryptedFile.Builder {
-    ctor public EncryptedFile.Builder(java.io.File, android.content.Context, String, androidx.security.crypto.EncryptedFile.FileEncryptionScheme);
+    ctor @Deprecated public EncryptedFile.Builder(java.io.File, android.content.Context, String, androidx.security.crypto.EncryptedFile.FileEncryptionScheme);
+    ctor public EncryptedFile.Builder(android.content.Context, java.io.File, androidx.security.crypto.MasterKey, androidx.security.crypto.EncryptedFile.FileEncryptionScheme);
     method public androidx.security.crypto.EncryptedFile build() throws java.security.GeneralSecurityException, java.io.IOException;
     method public androidx.security.crypto.EncryptedFile.Builder setKeysetAlias(String);
     method public androidx.security.crypto.EncryptedFile.Builder setKeysetPrefName(String);
@@ -19,7 +20,8 @@
 
   public final class EncryptedSharedPreferences implements android.content.SharedPreferences {
     method public boolean contains(String?);
-    method public static android.content.SharedPreferences create(String, String, android.content.Context, androidx.security.crypto.EncryptedSharedPreferences.PrefKeyEncryptionScheme, androidx.security.crypto.EncryptedSharedPreferences.PrefValueEncryptionScheme) throws java.security.GeneralSecurityException, java.io.IOException;
+    method public static android.content.SharedPreferences create(android.content.Context, String, androidx.security.crypto.MasterKey, androidx.security.crypto.EncryptedSharedPreferences.PrefKeyEncryptionScheme, androidx.security.crypto.EncryptedSharedPreferences.PrefValueEncryptionScheme) throws java.security.GeneralSecurityException, java.io.IOException;
+    method @Deprecated public static android.content.SharedPreferences create(String, String, android.content.Context, androidx.security.crypto.EncryptedSharedPreferences.PrefKeyEncryptionScheme, androidx.security.crypto.EncryptedSharedPreferences.PrefValueEncryptionScheme) throws java.security.GeneralSecurityException, java.io.IOException;
     method public android.content.SharedPreferences.Editor edit();
     method public java.util.Map<java.lang.String!,?> getAll();
     method public boolean getBoolean(String?, boolean);
@@ -40,9 +42,34 @@
     enum_constant public static final androidx.security.crypto.EncryptedSharedPreferences.PrefValueEncryptionScheme AES256_GCM;
   }
 
-  public final class MasterKeys {
-    method public static String getOrCreate(android.security.keystore.KeyGenParameterSpec) throws java.security.GeneralSecurityException, java.io.IOException;
-    field public static final android.security.keystore.KeyGenParameterSpec AES256_GCM_SPEC;
+  public final class MasterKey {
+    method public static int getDefaultAuthenticationValidityDurationSeconds();
+    method public int getUserAuthenticationValidityDurationSeconds();
+    method public boolean isKeyStoreBacked();
+    method public boolean isStrongBoxBacked();
+    method public boolean isUserAuthenticationRequired();
+    field public static final int DEFAULT_AES_GCM_MASTER_KEY_SIZE = 256; // 0x100
+    field public static final String DEFAULT_MASTER_KEY_ALIAS = "_androidx_security_master_key_";
+  }
+
+  public static final class MasterKey.Builder {
+    ctor public MasterKey.Builder(android.content.Context);
+    ctor public MasterKey.Builder(android.content.Context, String);
+    method public androidx.security.crypto.MasterKey build() throws java.security.GeneralSecurityException, java.io.IOException;
+    method @RequiresApi(android.os.Build.VERSION_CODES.M) public androidx.security.crypto.MasterKey.Builder setKeyGenParameterSpec(android.security.keystore.KeyGenParameterSpec);
+    method public androidx.security.crypto.MasterKey.Builder setKeyScheme(androidx.security.crypto.MasterKey.KeyScheme);
+    method public androidx.security.crypto.MasterKey.Builder setTryToUseStrongBox(boolean);
+    method public androidx.security.crypto.MasterKey.Builder setUserAuthenticationRequired(boolean);
+    method public androidx.security.crypto.MasterKey.Builder setUserAuthenticationRequired(boolean, @IntRange(from=1) int);
+  }
+
+  public enum MasterKey.KeyScheme {
+    enum_constant public static final androidx.security.crypto.MasterKey.KeyScheme AES256_GCM;
+  }
+
+  @Deprecated public final class MasterKeys {
+    method @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.M) public static String getOrCreate(android.security.keystore.KeyGenParameterSpec) throws java.security.GeneralSecurityException, java.io.IOException;
+    field @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.M) public static final android.security.keystore.KeyGenParameterSpec AES256_GCM_SPEC;
   }
 
 }
diff --git a/security/crypto/api/public_plus_experimental_1.1.0-alpha01.txt b/security/crypto/api/public_plus_experimental_1.1.0-alpha01.txt
index 3fb36a8..b6fa8f0 100644
--- a/security/crypto/api/public_plus_experimental_1.1.0-alpha01.txt
+++ b/security/crypto/api/public_plus_experimental_1.1.0-alpha01.txt
@@ -7,7 +7,8 @@
   }
 
   public static final class EncryptedFile.Builder {
-    ctor public EncryptedFile.Builder(java.io.File, android.content.Context, String, androidx.security.crypto.EncryptedFile.FileEncryptionScheme);
+    ctor @Deprecated public EncryptedFile.Builder(java.io.File, android.content.Context, String, androidx.security.crypto.EncryptedFile.FileEncryptionScheme);
+    ctor public EncryptedFile.Builder(android.content.Context, java.io.File, androidx.security.crypto.MasterKey, androidx.security.crypto.EncryptedFile.FileEncryptionScheme);
     method public androidx.security.crypto.EncryptedFile build() throws java.security.GeneralSecurityException, java.io.IOException;
     method public androidx.security.crypto.EncryptedFile.Builder setKeysetAlias(String);
     method public androidx.security.crypto.EncryptedFile.Builder setKeysetPrefName(String);
@@ -19,7 +20,8 @@
 
   public final class EncryptedSharedPreferences implements android.content.SharedPreferences {
     method public boolean contains(String?);
-    method public static android.content.SharedPreferences create(String, String, android.content.Context, androidx.security.crypto.EncryptedSharedPreferences.PrefKeyEncryptionScheme, androidx.security.crypto.EncryptedSharedPreferences.PrefValueEncryptionScheme) throws java.security.GeneralSecurityException, java.io.IOException;
+    method public static android.content.SharedPreferences create(android.content.Context, String, androidx.security.crypto.MasterKey, androidx.security.crypto.EncryptedSharedPreferences.PrefKeyEncryptionScheme, androidx.security.crypto.EncryptedSharedPreferences.PrefValueEncryptionScheme) throws java.security.GeneralSecurityException, java.io.IOException;
+    method @Deprecated public static android.content.SharedPreferences create(String, String, android.content.Context, androidx.security.crypto.EncryptedSharedPreferences.PrefKeyEncryptionScheme, androidx.security.crypto.EncryptedSharedPreferences.PrefValueEncryptionScheme) throws java.security.GeneralSecurityException, java.io.IOException;
     method public android.content.SharedPreferences.Editor edit();
     method public java.util.Map<java.lang.String!,?> getAll();
     method public boolean getBoolean(String?, boolean);
@@ -40,9 +42,34 @@
     enum_constant public static final androidx.security.crypto.EncryptedSharedPreferences.PrefValueEncryptionScheme AES256_GCM;
   }
 
-  public final class MasterKeys {
-    method public static String getOrCreate(android.security.keystore.KeyGenParameterSpec) throws java.security.GeneralSecurityException, java.io.IOException;
-    field public static final android.security.keystore.KeyGenParameterSpec AES256_GCM_SPEC;
+  public final class MasterKey {
+    method public static int getDefaultAuthenticationValidityDurationSeconds();
+    method public int getUserAuthenticationValidityDurationSeconds();
+    method public boolean isKeyStoreBacked();
+    method public boolean isStrongBoxBacked();
+    method public boolean isUserAuthenticationRequired();
+    field public static final int DEFAULT_AES_GCM_MASTER_KEY_SIZE = 256; // 0x100
+    field public static final String DEFAULT_MASTER_KEY_ALIAS = "_androidx_security_master_key_";
+  }
+
+  public static final class MasterKey.Builder {
+    ctor public MasterKey.Builder(android.content.Context);
+    ctor public MasterKey.Builder(android.content.Context, String);
+    method public androidx.security.crypto.MasterKey build() throws java.security.GeneralSecurityException, java.io.IOException;
+    method @RequiresApi(android.os.Build.VERSION_CODES.M) public androidx.security.crypto.MasterKey.Builder setKeyGenParameterSpec(android.security.keystore.KeyGenParameterSpec);
+    method public androidx.security.crypto.MasterKey.Builder setKeyScheme(androidx.security.crypto.MasterKey.KeyScheme);
+    method public androidx.security.crypto.MasterKey.Builder setTryToUseStrongBox(boolean);
+    method public androidx.security.crypto.MasterKey.Builder setUserAuthenticationRequired(boolean);
+    method public androidx.security.crypto.MasterKey.Builder setUserAuthenticationRequired(boolean, @IntRange(from=1) int);
+  }
+
+  public enum MasterKey.KeyScheme {
+    enum_constant public static final androidx.security.crypto.MasterKey.KeyScheme AES256_GCM;
+  }
+
+  @Deprecated public final class MasterKeys {
+    method @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.M) public static String getOrCreate(android.security.keystore.KeyGenParameterSpec) throws java.security.GeneralSecurityException, java.io.IOException;
+    field @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.M) public static final android.security.keystore.KeyGenParameterSpec AES256_GCM_SPEC;
   }
 
 }
diff --git a/security/crypto/api/public_plus_experimental_current.txt b/security/crypto/api/public_plus_experimental_current.txt
index 3fb36a8..b6fa8f0 100644
--- a/security/crypto/api/public_plus_experimental_current.txt
+++ b/security/crypto/api/public_plus_experimental_current.txt
@@ -7,7 +7,8 @@
   }
 
   public static final class EncryptedFile.Builder {
-    ctor public EncryptedFile.Builder(java.io.File, android.content.Context, String, androidx.security.crypto.EncryptedFile.FileEncryptionScheme);
+    ctor @Deprecated public EncryptedFile.Builder(java.io.File, android.content.Context, String, androidx.security.crypto.EncryptedFile.FileEncryptionScheme);
+    ctor public EncryptedFile.Builder(android.content.Context, java.io.File, androidx.security.crypto.MasterKey, androidx.security.crypto.EncryptedFile.FileEncryptionScheme);
     method public androidx.security.crypto.EncryptedFile build() throws java.security.GeneralSecurityException, java.io.IOException;
     method public androidx.security.crypto.EncryptedFile.Builder setKeysetAlias(String);
     method public androidx.security.crypto.EncryptedFile.Builder setKeysetPrefName(String);
@@ -19,7 +20,8 @@
 
   public final class EncryptedSharedPreferences implements android.content.SharedPreferences {
     method public boolean contains(String?);
-    method public static android.content.SharedPreferences create(String, String, android.content.Context, androidx.security.crypto.EncryptedSharedPreferences.PrefKeyEncryptionScheme, androidx.security.crypto.EncryptedSharedPreferences.PrefValueEncryptionScheme) throws java.security.GeneralSecurityException, java.io.IOException;
+    method public static android.content.SharedPreferences create(android.content.Context, String, androidx.security.crypto.MasterKey, androidx.security.crypto.EncryptedSharedPreferences.PrefKeyEncryptionScheme, androidx.security.crypto.EncryptedSharedPreferences.PrefValueEncryptionScheme) throws java.security.GeneralSecurityException, java.io.IOException;
+    method @Deprecated public static android.content.SharedPreferences create(String, String, android.content.Context, androidx.security.crypto.EncryptedSharedPreferences.PrefKeyEncryptionScheme, androidx.security.crypto.EncryptedSharedPreferences.PrefValueEncryptionScheme) throws java.security.GeneralSecurityException, java.io.IOException;
     method public android.content.SharedPreferences.Editor edit();
     method public java.util.Map<java.lang.String!,?> getAll();
     method public boolean getBoolean(String?, boolean);
@@ -40,9 +42,34 @@
     enum_constant public static final androidx.security.crypto.EncryptedSharedPreferences.PrefValueEncryptionScheme AES256_GCM;
   }
 
-  public final class MasterKeys {
-    method public static String getOrCreate(android.security.keystore.KeyGenParameterSpec) throws java.security.GeneralSecurityException, java.io.IOException;
-    field public static final android.security.keystore.KeyGenParameterSpec AES256_GCM_SPEC;
+  public final class MasterKey {
+    method public static int getDefaultAuthenticationValidityDurationSeconds();
+    method public int getUserAuthenticationValidityDurationSeconds();
+    method public boolean isKeyStoreBacked();
+    method public boolean isStrongBoxBacked();
+    method public boolean isUserAuthenticationRequired();
+    field public static final int DEFAULT_AES_GCM_MASTER_KEY_SIZE = 256; // 0x100
+    field public static final String DEFAULT_MASTER_KEY_ALIAS = "_androidx_security_master_key_";
+  }
+
+  public static final class MasterKey.Builder {
+    ctor public MasterKey.Builder(android.content.Context);
+    ctor public MasterKey.Builder(android.content.Context, String);
+    method public androidx.security.crypto.MasterKey build() throws java.security.GeneralSecurityException, java.io.IOException;
+    method @RequiresApi(android.os.Build.VERSION_CODES.M) public androidx.security.crypto.MasterKey.Builder setKeyGenParameterSpec(android.security.keystore.KeyGenParameterSpec);
+    method public androidx.security.crypto.MasterKey.Builder setKeyScheme(androidx.security.crypto.MasterKey.KeyScheme);
+    method public androidx.security.crypto.MasterKey.Builder setTryToUseStrongBox(boolean);
+    method public androidx.security.crypto.MasterKey.Builder setUserAuthenticationRequired(boolean);
+    method public androidx.security.crypto.MasterKey.Builder setUserAuthenticationRequired(boolean, @IntRange(from=1) int);
+  }
+
+  public enum MasterKey.KeyScheme {
+    enum_constant public static final androidx.security.crypto.MasterKey.KeyScheme AES256_GCM;
+  }
+
+  @Deprecated public final class MasterKeys {
+    method @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.M) public static String getOrCreate(android.security.keystore.KeyGenParameterSpec) throws java.security.GeneralSecurityException, java.io.IOException;
+    field @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.M) public static final android.security.keystore.KeyGenParameterSpec AES256_GCM_SPEC;
   }
 
 }
diff --git a/security/crypto/api/restricted_1.1.0-alpha01.txt b/security/crypto/api/restricted_1.1.0-alpha01.txt
index 3fb36a8..b6fa8f0 100644
--- a/security/crypto/api/restricted_1.1.0-alpha01.txt
+++ b/security/crypto/api/restricted_1.1.0-alpha01.txt
@@ -7,7 +7,8 @@
   }
 
   public static final class EncryptedFile.Builder {
-    ctor public EncryptedFile.Builder(java.io.File, android.content.Context, String, androidx.security.crypto.EncryptedFile.FileEncryptionScheme);
+    ctor @Deprecated public EncryptedFile.Builder(java.io.File, android.content.Context, String, androidx.security.crypto.EncryptedFile.FileEncryptionScheme);
+    ctor public EncryptedFile.Builder(android.content.Context, java.io.File, androidx.security.crypto.MasterKey, androidx.security.crypto.EncryptedFile.FileEncryptionScheme);
     method public androidx.security.crypto.EncryptedFile build() throws java.security.GeneralSecurityException, java.io.IOException;
     method public androidx.security.crypto.EncryptedFile.Builder setKeysetAlias(String);
     method public androidx.security.crypto.EncryptedFile.Builder setKeysetPrefName(String);
@@ -19,7 +20,8 @@
 
   public final class EncryptedSharedPreferences implements android.content.SharedPreferences {
     method public boolean contains(String?);
-    method public static android.content.SharedPreferences create(String, String, android.content.Context, androidx.security.crypto.EncryptedSharedPreferences.PrefKeyEncryptionScheme, androidx.security.crypto.EncryptedSharedPreferences.PrefValueEncryptionScheme) throws java.security.GeneralSecurityException, java.io.IOException;
+    method public static android.content.SharedPreferences create(android.content.Context, String, androidx.security.crypto.MasterKey, androidx.security.crypto.EncryptedSharedPreferences.PrefKeyEncryptionScheme, androidx.security.crypto.EncryptedSharedPreferences.PrefValueEncryptionScheme) throws java.security.GeneralSecurityException, java.io.IOException;
+    method @Deprecated public static android.content.SharedPreferences create(String, String, android.content.Context, androidx.security.crypto.EncryptedSharedPreferences.PrefKeyEncryptionScheme, androidx.security.crypto.EncryptedSharedPreferences.PrefValueEncryptionScheme) throws java.security.GeneralSecurityException, java.io.IOException;
     method public android.content.SharedPreferences.Editor edit();
     method public java.util.Map<java.lang.String!,?> getAll();
     method public boolean getBoolean(String?, boolean);
@@ -40,9 +42,34 @@
     enum_constant public static final androidx.security.crypto.EncryptedSharedPreferences.PrefValueEncryptionScheme AES256_GCM;
   }
 
-  public final class MasterKeys {
-    method public static String getOrCreate(android.security.keystore.KeyGenParameterSpec) throws java.security.GeneralSecurityException, java.io.IOException;
-    field public static final android.security.keystore.KeyGenParameterSpec AES256_GCM_SPEC;
+  public final class MasterKey {
+    method public static int getDefaultAuthenticationValidityDurationSeconds();
+    method public int getUserAuthenticationValidityDurationSeconds();
+    method public boolean isKeyStoreBacked();
+    method public boolean isStrongBoxBacked();
+    method public boolean isUserAuthenticationRequired();
+    field public static final int DEFAULT_AES_GCM_MASTER_KEY_SIZE = 256; // 0x100
+    field public static final String DEFAULT_MASTER_KEY_ALIAS = "_androidx_security_master_key_";
+  }
+
+  public static final class MasterKey.Builder {
+    ctor public MasterKey.Builder(android.content.Context);
+    ctor public MasterKey.Builder(android.content.Context, String);
+    method public androidx.security.crypto.MasterKey build() throws java.security.GeneralSecurityException, java.io.IOException;
+    method @RequiresApi(android.os.Build.VERSION_CODES.M) public androidx.security.crypto.MasterKey.Builder setKeyGenParameterSpec(android.security.keystore.KeyGenParameterSpec);
+    method public androidx.security.crypto.MasterKey.Builder setKeyScheme(androidx.security.crypto.MasterKey.KeyScheme);
+    method public androidx.security.crypto.MasterKey.Builder setTryToUseStrongBox(boolean);
+    method public androidx.security.crypto.MasterKey.Builder setUserAuthenticationRequired(boolean);
+    method public androidx.security.crypto.MasterKey.Builder setUserAuthenticationRequired(boolean, @IntRange(from=1) int);
+  }
+
+  public enum MasterKey.KeyScheme {
+    enum_constant public static final androidx.security.crypto.MasterKey.KeyScheme AES256_GCM;
+  }
+
+  @Deprecated public final class MasterKeys {
+    method @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.M) public static String getOrCreate(android.security.keystore.KeyGenParameterSpec) throws java.security.GeneralSecurityException, java.io.IOException;
+    field @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.M) public static final android.security.keystore.KeyGenParameterSpec AES256_GCM_SPEC;
   }
 
 }
diff --git a/security/crypto/api/restricted_current.txt b/security/crypto/api/restricted_current.txt
index 3fb36a8..b6fa8f0 100644
--- a/security/crypto/api/restricted_current.txt
+++ b/security/crypto/api/restricted_current.txt
@@ -7,7 +7,8 @@
   }
 
   public static final class EncryptedFile.Builder {
-    ctor public EncryptedFile.Builder(java.io.File, android.content.Context, String, androidx.security.crypto.EncryptedFile.FileEncryptionScheme);
+    ctor @Deprecated public EncryptedFile.Builder(java.io.File, android.content.Context, String, androidx.security.crypto.EncryptedFile.FileEncryptionScheme);
+    ctor public EncryptedFile.Builder(android.content.Context, java.io.File, androidx.security.crypto.MasterKey, androidx.security.crypto.EncryptedFile.FileEncryptionScheme);
     method public androidx.security.crypto.EncryptedFile build() throws java.security.GeneralSecurityException, java.io.IOException;
     method public androidx.security.crypto.EncryptedFile.Builder setKeysetAlias(String);
     method public androidx.security.crypto.EncryptedFile.Builder setKeysetPrefName(String);
@@ -19,7 +20,8 @@
 
   public final class EncryptedSharedPreferences implements android.content.SharedPreferences {
     method public boolean contains(String?);
-    method public static android.content.SharedPreferences create(String, String, android.content.Context, androidx.security.crypto.EncryptedSharedPreferences.PrefKeyEncryptionScheme, androidx.security.crypto.EncryptedSharedPreferences.PrefValueEncryptionScheme) throws java.security.GeneralSecurityException, java.io.IOException;
+    method public static android.content.SharedPreferences create(android.content.Context, String, androidx.security.crypto.MasterKey, androidx.security.crypto.EncryptedSharedPreferences.PrefKeyEncryptionScheme, androidx.security.crypto.EncryptedSharedPreferences.PrefValueEncryptionScheme) throws java.security.GeneralSecurityException, java.io.IOException;
+    method @Deprecated public static android.content.SharedPreferences create(String, String, android.content.Context, androidx.security.crypto.EncryptedSharedPreferences.PrefKeyEncryptionScheme, androidx.security.crypto.EncryptedSharedPreferences.PrefValueEncryptionScheme) throws java.security.GeneralSecurityException, java.io.IOException;
     method public android.content.SharedPreferences.Editor edit();
     method public java.util.Map<java.lang.String!,?> getAll();
     method public boolean getBoolean(String?, boolean);
@@ -40,9 +42,34 @@
     enum_constant public static final androidx.security.crypto.EncryptedSharedPreferences.PrefValueEncryptionScheme AES256_GCM;
   }
 
-  public final class MasterKeys {
-    method public static String getOrCreate(android.security.keystore.KeyGenParameterSpec) throws java.security.GeneralSecurityException, java.io.IOException;
-    field public static final android.security.keystore.KeyGenParameterSpec AES256_GCM_SPEC;
+  public final class MasterKey {
+    method public static int getDefaultAuthenticationValidityDurationSeconds();
+    method public int getUserAuthenticationValidityDurationSeconds();
+    method public boolean isKeyStoreBacked();
+    method public boolean isStrongBoxBacked();
+    method public boolean isUserAuthenticationRequired();
+    field public static final int DEFAULT_AES_GCM_MASTER_KEY_SIZE = 256; // 0x100
+    field public static final String DEFAULT_MASTER_KEY_ALIAS = "_androidx_security_master_key_";
+  }
+
+  public static final class MasterKey.Builder {
+    ctor public MasterKey.Builder(android.content.Context);
+    ctor public MasterKey.Builder(android.content.Context, String);
+    method public androidx.security.crypto.MasterKey build() throws java.security.GeneralSecurityException, java.io.IOException;
+    method @RequiresApi(android.os.Build.VERSION_CODES.M) public androidx.security.crypto.MasterKey.Builder setKeyGenParameterSpec(android.security.keystore.KeyGenParameterSpec);
+    method public androidx.security.crypto.MasterKey.Builder setKeyScheme(androidx.security.crypto.MasterKey.KeyScheme);
+    method public androidx.security.crypto.MasterKey.Builder setTryToUseStrongBox(boolean);
+    method public androidx.security.crypto.MasterKey.Builder setUserAuthenticationRequired(boolean);
+    method public androidx.security.crypto.MasterKey.Builder setUserAuthenticationRequired(boolean, @IntRange(from=1) int);
+  }
+
+  public enum MasterKey.KeyScheme {
+    enum_constant public static final androidx.security.crypto.MasterKey.KeyScheme AES256_GCM;
+  }
+
+  @Deprecated public final class MasterKeys {
+    method @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.M) public static String getOrCreate(android.security.keystore.KeyGenParameterSpec) throws java.security.GeneralSecurityException, java.io.IOException;
+    field @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.M) public static final android.security.keystore.KeyGenParameterSpec AES256_GCM_SPEC;
   }
 
 }
diff --git a/security/crypto/build.gradle b/security/crypto/build.gradle
index 2bd04a6..5ec42a0 100644
--- a/security/crypto/build.gradle
+++ b/security/crypto/build.gradle
@@ -29,6 +29,7 @@
     api("androidx.annotation:annotation:1.1.0")
 
     implementation("com.google.crypto.tink:tink-android:1.4.0-rc2")
+    implementation(project(":collection:collection"))
 
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_CORE)
@@ -40,7 +41,7 @@
 
 android {
     defaultConfig {
-        minSdkVersion 23
+        minSdkVersion 21
     }
 }
 
diff --git a/security/crypto/src/androidTest/java/androidx/security/crypto/EncryptedFileTest.java b/security/crypto/src/androidTest/java/androidx/security/crypto/EncryptedFileTest.java
index 301a4d2..e22f961 100644
--- a/security/crypto/src/androidTest/java/androidx/security/crypto/EncryptedFileTest.java
+++ b/security/crypto/src/androidTest/java/androidx/security/crypto/EncryptedFileTest.java
@@ -16,7 +16,7 @@
 
 package androidx.security.crypto;
 
-import static androidx.security.crypto.MasterKeys.KEYSTORE_PATH_URI;
+import static androidx.security.crypto.MasterKey.KEYSTORE_PATH_URI;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 
@@ -52,7 +52,7 @@
 public class EncryptedFileTest {
 
     private Context mContext;
-    private String mMasterKeyAlias;
+    private MasterKey mMasterKey;
 
     @Before
     public void setup() throws Exception {
@@ -87,7 +87,9 @@
         keyStore.load(null);
         keyStore.deleteEntry(MasterKeys.MASTER_KEY_ALIAS);
 
-        mMasterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC);
+        mMasterKey = new MasterKey.Builder(mContext)
+                .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
+                .build();
     }
 
     @Test
@@ -97,8 +99,83 @@
 
         // Write
 
+        EncryptedFile encryptedFile = new EncryptedFile.Builder(mContext,
+                new File(mContext.getFilesDir(),
+                fileName), mMasterKey,
+                EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB)
+                .build();
+
+        OutputStream outputStream = encryptedFile.openFileOutput();
+        outputStream.write(fileContent.getBytes("UTF-8"));
+        outputStream.flush();
+        outputStream.close();
+
+        FileInputStream rawStream = mContext.openFileInput(fileName);
+        ByteArrayOutputStream rawByteArrayOutputStream = new ByteArrayOutputStream();
+        int rawNextByte = rawStream.read();
+        while (rawNextByte != -1) {
+            rawByteArrayOutputStream.write(rawNextByte);
+            rawNextByte = rawStream.read();
+        }
+        byte[] rawCipherText = rawByteArrayOutputStream.toByteArray();
+        System.out.println("Raw CipherText = " + new String(rawCipherText,
+                UTF_8));
+        rawStream.close();
+
+        InputStream inputStream = encryptedFile.openFileInput();
+        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+        int nextByte = inputStream.read();
+        while (nextByte != -1) {
+            byteArrayOutputStream.write(nextByte);
+            nextByte = inputStream.read();
+        }
+
+        byte[] plainText = byteArrayOutputStream.toByteArray();
+
+        System.out.println("Decrypted Data: " + new String(plainText,
+                UTF_8));
+
+        Assert.assertEquals(
+                "Contents should be equal, data was encrypted.",
+                fileContent, new String(plainText, "UTF-8"));
+        inputStream.close();
+
+
+        EncryptedFile existingFileInputCheck = new EncryptedFile.Builder(mContext,
+                new File(mContext.getFilesDir(), "FAKE_FILE"), mMasterKey,
+                EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB)
+                .build();
+        boolean inputFailed = false;
+        try {
+            existingFileInputCheck.openFileInput();
+        } catch (IOException ex) {
+            inputFailed = true;
+        }
+        Assert.assertTrue("File should have failed opening.", inputFailed);
+
+        EncryptedFile existingFileOutputCheck = new EncryptedFile.Builder(mContext,
+                new File(mContext.getFilesDir(), fileName), mMasterKey,
+                EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB)
+                .build();
+        boolean outputFailed = false;
+        try {
+            existingFileOutputCheck.openFileOutput();
+        } catch (IOException ex) {
+            outputFailed = true;
+        }
+        Assert.assertTrue("File should have failed writing.", outputFailed);
+
+    }
+
+    @Test
+    public void testWriteReadEncryptedFileWithAlias() throws Exception {
+        final String fileContent = "Don't tell anyone...";
+        final String fileName = "nothing_to_see_here";
+
+        // Write
+
         EncryptedFile encryptedFile = new EncryptedFile.Builder(new File(mContext.getFilesDir(),
-                fileName), mContext, mMasterKeyAlias,
+                        fileName), mContext, mMasterKey.getKeyAlias(),
                 EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB)
                 .build();
 
@@ -139,8 +216,8 @@
 
 
         EncryptedFile existingFileInputCheck = new EncryptedFile.Builder(
-                new File(mContext.getFilesDir(), "FAKE_FILE"), mContext, mMasterKeyAlias,
-                EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB)
+                new File(mContext.getFilesDir(), "FAKE_FILE"), mContext,
+                mMasterKey.getKeyAlias(), EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB)
                 .build();
         boolean inputFailed = false;
         try {
@@ -151,7 +228,7 @@
         Assert.assertTrue("File should have failed opening.", inputFailed);
 
         EncryptedFile existingFileOutputCheck = new EncryptedFile.Builder(
-                new File(mContext.getFilesDir(), fileName), mContext, mMasterKeyAlias,
+                new File(mContext.getFilesDir(), fileName), mContext, mMasterKey.getKeyAlias(),
                 EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB)
                 .build();
         boolean outputFailed = false;
@@ -170,8 +247,9 @@
         final String fileName = "nothing_to_see_here_custom";
 
         // Write
-        EncryptedFile encryptedFile = new EncryptedFile.Builder(new File(mContext.getFilesDir(),
-                fileName), mContext, mMasterKeyAlias,
+        EncryptedFile encryptedFile = new EncryptedFile.Builder(mContext,
+                new File(mContext.getFilesDir(),
+                fileName), mMasterKey,
                 EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB)
                 .setKeysetAlias("CustomKEYALIAS")
                 .setKeysetPrefName("CUSTOMPREFNAME")
@@ -225,7 +303,7 @@
         File file = new File(mContext.getFilesDir(), fileName);
 
         // Write
-        EncryptedFile encryptedFile = new EncryptedFile.Builder(file, mContext, mMasterKeyAlias,
+        EncryptedFile encryptedFile = new EncryptedFile.Builder(mContext, file, mMasterKey,
                 EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB)
                 .build();
 
@@ -240,7 +318,7 @@
                 .withSharedPref(mContext,
                         "__androidx_security_crypto_encrypted_file_keyset__",
                         "__androidx_security_crypto_encrypted_file_pref__")
-                .withMasterKeyUri(KEYSTORE_PATH_URI + mMasterKeyAlias)
+                .withMasterKeyUri(KEYSTORE_PATH_URI + mMasterKey.getKeyAlias())
                 .build().getKeysetHandle();
 
         StreamingAead streamingAead = StreamingAeadFactory.getPrimitive(
diff --git a/security/crypto/src/androidTest/java/androidx/security/crypto/EncryptedSharedPreferencesTest.java b/security/crypto/src/androidTest/java/androidx/security/crypto/EncryptedSharedPreferencesTest.java
index 5e9f0ad..dc80a78 100644
--- a/security/crypto/src/androidTest/java/androidx/security/crypto/EncryptedSharedPreferencesTest.java
+++ b/security/crypto/src/androidTest/java/androidx/security/crypto/EncryptedSharedPreferencesTest.java
@@ -18,7 +18,7 @@
 
 import static android.content.Context.MODE_PRIVATE;
 
-import static androidx.security.crypto.MasterKeys.KEYSTORE_PATH_URI;
+import static androidx.security.crypto.MasterKey.KEYSTORE_PATH_URI;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 
@@ -57,7 +57,7 @@
 public class EncryptedSharedPreferencesTest {
 
     private Context mContext;
-    private String mKeyAlias;
+    private MasterKey mMasterKey;
 
     private static final String PREFS_FILE = "test_shared_prefs";
 
@@ -101,15 +101,18 @@
         keyStore.load(null);
         keyStore.deleteEntry("_androidx_security_master_key_");
 
-        mKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC);
+        mMasterKey = new MasterKey.Builder(mContext)
+                .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
+                .build();
     }
 
     @Test
     public void testWriteSharedPrefs() throws Exception {
 
         SharedPreferences sharedPreferences = EncryptedSharedPreferences
-                .create(PREFS_FILE,
-                        mKeyAlias, mContext,
+                .create(mContext,
+                        PREFS_FILE,
+                        mMasterKey,
                         EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
                         EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM);
 
@@ -341,8 +344,9 @@
         String testValue = "TestValue";
 
         SharedPreferences encryptedSharedPreferences = EncryptedSharedPreferences
-                .create(tinkTestPrefs,
-                        mKeyAlias, mContext,
+                .create(mContext,
+                        tinkTestPrefs,
+                        mMasterKey,
                         EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
                         EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM);
 
diff --git a/security/crypto/src/androidTest/java/androidx/security/crypto/MasterKeySecureTest.java b/security/crypto/src/androidTest/java/androidx/security/crypto/MasterKeySecureTest.java
new file mode 100644
index 0000000..fc78abb
--- /dev/null
+++ b/security/crypto/src/androidTest/java/androidx/security/crypto/MasterKeySecureTest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 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 androidx.security.crypto;
+
+import static android.content.Context.MODE_PRIVATE;
+
+import android.app.KeyguardManager;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Build;
+
+import androidx.security.crypto.MasterKey.KeyScheme;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.SdkSuppress;
+
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+
+/**
+ * These are tests that require the device to have a lockscreen enabled.
+ */
+@MediumTest
+@RunWith(JUnit4.class)
+public class MasterKeySecureTest {
+    private static final String PREFS_FILE = "test_shared_prefs";
+    private static final int KEY_SIZE = 256;
+
+    @Before
+    @SuppressWarnings("ResultOfMethodCallIgnored")
+    public void setup() throws Exception {
+        final Context context = ApplicationProvider.getApplicationContext();
+
+        KeyguardManager keyguardManager = context.getSystemService(KeyguardManager.class);
+        Assume.assumeTrue(keyguardManager != null && keyguardManager.isDeviceSecure());
+
+        // Delete all previous keys and shared preferences.
+
+        String filePath = context.getFilesDir().getParent() + "/shared_prefs/"
+                + "__androidx_security__crypto_encrypted_prefs__";
+        File deletePrefFile = new File(filePath);
+        deletePrefFile.delete();
+
+        SharedPreferences notEncryptedSharedPrefs = context.getSharedPreferences(PREFS_FILE,
+                MODE_PRIVATE);
+        notEncryptedSharedPrefs.edit().clear().commit();
+
+        filePath = context.getFilesDir().getParent() + "/shared_prefs/"
+                + PREFS_FILE;
+        deletePrefFile = new File(filePath);
+        deletePrefFile.delete();
+
+        SharedPreferences encryptedSharedPrefs = context.getSharedPreferences("TinkTestPrefs",
+                MODE_PRIVATE);
+        encryptedSharedPrefs.edit().clear().commit();
+
+        filePath = context.getFilesDir().getParent() + "/shared_prefs/"
+                + "TinkTestPrefs";
+        deletePrefFile = new File(filePath);
+        deletePrefFile.delete();
+
+        // Delete MasterKeys
+        KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+        keyStore.load(null);
+        keyStore.deleteEntry("_androidx_security_master_key_");
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.M)
+    @Test
+    public void testCreateKeyWithAuthenicationRequired() throws GeneralSecurityException,
+            IOException {
+        MasterKey masterKey = new MasterKey.Builder(ApplicationProvider.getApplicationContext())
+                .setKeyScheme(KeyScheme.AES256_GCM)
+                .setUserAuthenticationRequired(true, 10)
+                .build();
+        MasterKeyTest.assertKeyExists(masterKey.getKeyAlias());
+        Assert.assertTrue(masterKey.isUserAuthenticationRequired());
+        Assert.assertEquals(masterKey.getUserAuthenticationValidityDurationSeconds(), 10);
+    }
+}
diff --git a/security/crypto/src/androidTest/java/androidx/security/crypto/MasterKeyTest.java b/security/crypto/src/androidTest/java/androidx/security/crypto/MasterKeyTest.java
new file mode 100644
index 0000000..ee67c2f
--- /dev/null
+++ b/security/crypto/src/androidTest/java/androidx/security/crypto/MasterKeyTest.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright 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 androidx.security.crypto;
+
+import static android.content.Context.MODE_PRIVATE;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Build;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+
+import androidx.security.crypto.MasterKey.KeyScheme;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.SdkSuppress;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+
+@MediumTest
+@RunWith(JUnit4.class)
+public class MasterKeyTest {
+    private static final String PREFS_FILE = "test_shared_prefs";
+    private static final int KEY_SIZE = 256;
+
+    @Before
+    @SuppressWarnings("ResultOfMethodCallIgnored")
+    public void setup() throws Exception {
+
+        final Context context = ApplicationProvider.getApplicationContext();
+
+        // Delete all previous keys and shared preferences.
+
+        String filePath = context.getFilesDir().getParent() + "/shared_prefs/"
+                + "__androidx_security__crypto_encrypted_prefs__";
+        File deletePrefFile = new File(filePath);
+        deletePrefFile.delete();
+
+        SharedPreferences notEncryptedSharedPrefs = context.getSharedPreferences(PREFS_FILE,
+                MODE_PRIVATE);
+        notEncryptedSharedPrefs.edit().clear().commit();
+
+        filePath = context.getFilesDir().getParent() + "/shared_prefs/"
+                + PREFS_FILE;
+        deletePrefFile = new File(filePath);
+        deletePrefFile.delete();
+
+        SharedPreferences encryptedSharedPrefs = context.getSharedPreferences("TinkTestPrefs",
+                MODE_PRIVATE);
+        encryptedSharedPrefs.edit().clear().commit();
+
+        filePath = context.getFilesDir().getParent() + "/shared_prefs/"
+                + "TinkTestPrefs";
+        deletePrefFile = new File(filePath);
+        deletePrefFile.delete();
+
+        // Delete MasterKeys
+        KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+        keyStore.load(null);
+        keyStore.deleteEntry("_androidx_security_master_key_");
+    }
+
+    @Test
+    public void testCreateDefaultKey() throws GeneralSecurityException, IOException {
+        MasterKey masterKey = new MasterKey.Builder(ApplicationProvider.getApplicationContext())
+                .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
+                .build();
+        assertKeyExists(masterKey.getKeyAlias());
+    }
+
+    @Test
+    public void testCreateRenamedKey() throws GeneralSecurityException, IOException {
+        final String testAlias = "TestKeyAlias";
+        MasterKey masterKey =
+                new MasterKey.Builder(ApplicationProvider.getApplicationContext(), testAlias)
+                        .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
+                        .build();
+        Assert.assertEquals(masterKey.getKeyAlias(), testAlias);
+        assertKeyExists(masterKey.getKeyAlias());
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.M)
+    @Test
+    public void testCreateKeyWithParamSpec() throws GeneralSecurityException, IOException {
+        KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
+                MasterKey.DEFAULT_MASTER_KEY_ALIAS,
+                KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+                .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
+                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+                .setKeySize(KEY_SIZE)
+                .build();
+        MasterKey masterKey = new MasterKey.Builder(ApplicationProvider.getApplicationContext())
+                .setKeyGenParameterSpec(spec)
+                .build();
+        assertKeyExists(masterKey.getKeyAlias());
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.M)
+    @Test
+    public void testCreateKeyWithParamSpecAndAlias() throws GeneralSecurityException,
+            IOException {
+        KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder("test_key_alias",
+                KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+                .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
+                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+                .setKeySize(KEY_SIZE)
+                .build();
+        MasterKey masterKey = new MasterKey.Builder(
+                ApplicationProvider.getApplicationContext(), "test_key_alias")
+                .setKeyGenParameterSpec(spec)
+                .build();
+        assertKeyExists(masterKey.getKeyAlias());
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.M)
+    @Test
+    public void testCreateKeyWithParamSpecWithDifferentAliasFails() throws GeneralSecurityException,
+            IOException {
+        KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder("test_key_alias",
+                KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+                .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
+                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+                .setKeySize(KEY_SIZE)
+                .build();
+        try {
+            MasterKey masterKey = new MasterKey.Builder(ApplicationProvider.getApplicationContext())
+                    .setKeyGenParameterSpec(spec)
+                    .build();
+            Assert.fail("Could create key with inconsistent key alias");
+        } catch (IllegalArgumentException iae) {
+            // Pass
+        }
+    }
+
+    @Test
+    public void testCheckIfKeyIsKeyStoreBacked() throws GeneralSecurityException,
+            IOException {
+        MasterKey masterKey = new MasterKey.Builder(ApplicationProvider.getApplicationContext())
+                .setKeyScheme(KeyScheme.AES256_GCM)
+                .build();
+
+        KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+        keyStore.load(null);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            Assert.assertTrue(masterKey.isKeyStoreBacked());
+            assertKeyExists(masterKey.getKeyAlias());
+        }
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.M)
+    @Test
+    public void testUseOfSchemeAndParamsFails() throws GeneralSecurityException,
+            IOException {
+        KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(MasterKeys.MASTER_KEY_ALIAS,
+                KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+                .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
+                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+                .setKeySize(KEY_SIZE)
+                .build();
+
+        try {
+            MasterKey masterKey = new MasterKey.Builder(ApplicationProvider.getApplicationContext())
+                    .setKeyScheme(KeyScheme.AES256_GCM)
+                    .setKeyGenParameterSpec(spec)
+                    .build();
+            Assert.fail("Could create key with both scheme + KeyGenParameterSpec");
+        } catch (IllegalArgumentException iae) {
+            // Pass.
+        }
+    }
+
+    @Test
+    public void testCheckGettersAreCallable() throws GeneralSecurityException,
+            IOException {
+        MasterKey masterKey = new MasterKey.Builder(ApplicationProvider.getApplicationContext())
+                .setKeyScheme(KeyScheme.AES256_GCM)
+                .build();
+        Assert.assertFalse(masterKey.isUserAuthenticationRequired());
+        Assert.assertFalse(masterKey.isStrongBoxBacked());
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+            Assert.assertEquals(masterKey.getUserAuthenticationValidityDurationSeconds(), 0);
+        }
+    }
+
+    static void assertKeyExists(String keyAlias) {
+        try {
+            KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+            keyStore.load(null);
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                Assert.assertTrue(keyStore.isKeyEntry(keyAlias));
+            } else {
+                // Key shouldn't exist on Lollipop =o
+                Assert.assertFalse(keyStore.isKeyEntry(keyAlias));
+            }
+        } catch (Exception e) {
+            Assert.fail("Exception checking for key: " + keyAlias);
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/security/crypto/src/androidTest/java/androidx/security/crypto/MasterKeysTest.java b/security/crypto/src/androidTest/java/androidx/security/crypto/MasterKeysTest.java
index 140619a..ed8e695 100644
--- a/security/crypto/src/androidTest/java/androidx/security/crypto/MasterKeysTest.java
+++ b/security/crypto/src/androidTest/java/androidx/security/crypto/MasterKeysTest.java
@@ -20,11 +20,13 @@
 
 import android.content.Context;
 import android.content.SharedPreferences;
+import android.os.Build;
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
 
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.filters.MediumTest;
+import androidx.test.filters.SdkSuppress;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -38,6 +40,7 @@
 import java.security.KeyStore;
 
 @MediumTest
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.M)
 @RunWith(JUnit4.class)
 public class MasterKeysTest {
     private static final String PREFS_FILE = "test_shared_prefs";
diff --git a/security/crypto/src/main/java/androidx/security/crypto/EncryptedFile.java b/security/crypto/src/main/java/androidx/security/crypto/EncryptedFile.java
index 76e886a..5fa72eb 100644
--- a/security/crypto/src/main/java/androidx/security/crypto/EncryptedFile.java
+++ b/security/crypto/src/main/java/androidx/security/crypto/EncryptedFile.java
@@ -16,10 +16,11 @@
 
 package androidx.security.crypto;
 
-import static androidx.security.crypto.MasterKeys.KEYSTORE_PATH_URI;
+import static androidx.security.crypto.MasterKey.KEYSTORE_PATH_URI;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 
+import android.annotation.SuppressLint;
 import android.content.Context;
 
 import androidx.annotation.NonNull;
@@ -61,7 +62,6 @@
  *  // read the encrypted file
  *  FileInputStream encryptedInputStream = encryptedFile.openFileInput();
  * </pre>
- *
  */
 public final class EncryptedFile {
 
@@ -75,7 +75,6 @@
     final String mMasterKeyAlias;
     final StreamingAead mStreamingAead;
 
-
     EncryptedFile(
             @NonNull File file,
             @NonNull String masterKeyAlias,
@@ -118,16 +117,45 @@
      */
     public static final class Builder {
 
+        /**
+         * Builder for an EncryptedFile.
+         *
+         * @deprecated Use {@link #Builder(Context, File, MasterKey, FileEncryptionScheme)} instead.
+         */
+        @Deprecated
         public Builder(@NonNull File file,
                 @NonNull Context context,
                 @NonNull String masterKeyAlias,
                 @NonNull FileEncryptionScheme fileEncryptionScheme) {
             mFile = file;
             mFileEncryptionScheme = fileEncryptionScheme;
-            mContext = context;
+            mContext = context.getApplicationContext();
             mMasterKeyAlias = masterKeyAlias;
         }
 
+        /**
+         * Builder for an EncryptedFile.
+         */
+        // [StreamFiles]: Because the contents of EncryptedFile are encrypted the use of
+        // a FileDescriptor or Streams are intentionally not supported for the following reasons:
+        // - The encrypted content is tightly coupled to the current installation of the app. If
+        // the app is uninstalled, even if the data remained (such as being stored in a public
+        // directory or another DocumentProvider) it would be (intentionally) unrecoverable.
+        // - If the API did accept either an already opened FileDescriptor or a stream, then it
+        // would be possible for the developer to inadvertently commingle encrypted and plain
+        // text data, which, due to the way the API is structured, could render both encrypted
+        // and unencrypted data irrecoverable.
+        @SuppressLint("StreamFiles")
+        public Builder(@NonNull Context context,
+                @NonNull File file,
+                @NonNull MasterKey masterKey,
+                @NonNull FileEncryptionScheme fileEncryptionScheme) {
+            mFile = file;
+            mFileEncryptionScheme = fileEncryptionScheme;
+            mContext = context.getApplicationContext();
+            mMasterKeyAlias = masterKey.getKeyAlias();
+        }
+
         // Required parameters
         File mFile;
         final FileEncryptionScheme mFileEncryptionScheme;
@@ -187,7 +215,7 @@
      *
      * @return The FileOutputStream that encrypts all data.
      * @throws GeneralSecurityException when a bad master key or keyset has been used
-     * @throws IOException when the file already exists or is not available for writing
+     * @throws IOException              when the file already exists or is not available for writing
      */
     @NonNull
     public FileOutputStream openFileOutput()
@@ -210,7 +238,7 @@
      *
      * @return The input stream to read previously encrypted data.
      * @throws GeneralSecurityException when a bad master key or keyset has been used
-     * @throws IOException when the file was not found
+     * @throws IOException              when the file was not found
      */
     @NonNull
     public FileInputStream openFileInput()
@@ -226,7 +254,6 @@
 
     /**
      * Encrypted file output stream
-     *
      */
     private static final class EncryptedFileOutputStream extends FileOutputStream {
 
diff --git a/security/crypto/src/main/java/androidx/security/crypto/EncryptedSharedPreferences.java b/security/crypto/src/main/java/androidx/security/crypto/EncryptedSharedPreferences.java
index 5223548..80a1320 100644
--- a/security/crypto/src/main/java/androidx/security/crypto/EncryptedSharedPreferences.java
+++ b/security/crypto/src/main/java/androidx/security/crypto/EncryptedSharedPreferences.java
@@ -16,17 +16,17 @@
 
 package androidx.security.crypto;
 
-import static androidx.security.crypto.MasterKeys.KEYSTORE_PATH_URI;
+import static androidx.security.crypto.MasterKey.KEYSTORE_PATH_URI;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 
 import android.content.Context;
 import android.content.SharedPreferences;
-import android.util.ArraySet;
 import android.util.Pair;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.collection.ArraySet;
 
 import com.google.crypto.tink.Aead;
 import com.google.crypto.tink.DeterministicAead;
@@ -100,12 +100,43 @@
     /**
      * Opens an instance of encrypted SharedPreferences
      *
-     * @param fileName The name of the file to open; can not contain path separators.
+     * @param fileName                  The name of the file to open; can not contain path
+     *                                  separators.
+     * @param masterKey                 The master key to use.
+     * @param prefKeyEncryptionScheme   The scheme to use for encrypting keys.
+     * @param prefValueEncryptionScheme The scheme to use for encrypting values.
      * @return The SharedPreferences instance that encrypts all data.
      * @throws GeneralSecurityException when a bad master key or keyset has been attempted
      * @throws IOException              when fileName can not be used
      */
     @NonNull
+    public static SharedPreferences create(@NonNull Context context,
+            @NonNull String fileName,
+            @NonNull MasterKey masterKey,
+            @NonNull PrefKeyEncryptionScheme prefKeyEncryptionScheme,
+            @NonNull PrefValueEncryptionScheme prefValueEncryptionScheme)
+            throws GeneralSecurityException, IOException {
+        return create(fileName, masterKey.getKeyAlias(), context,
+                prefKeyEncryptionScheme, prefValueEncryptionScheme);
+    }
+
+    /**
+     * Opens an instance of encrypted SharedPreferences
+     *
+     * @param fileName                  The name of the file to open; can not contain path
+     *                                  separators.
+     * @param masterKeyAlias            The alias of the master key to use.
+     * @param context                   The context to use to open the preferences file.
+     * @param prefKeyEncryptionScheme   The scheme to use for encrypting keys.
+     * @param prefValueEncryptionScheme The scheme to use for encrypting values.
+     * @return The SharedPreferences instance that encrypts all data.
+     * @throws GeneralSecurityException when a bad master key or keyset has been attempted
+     * @throws IOException              when fileName can not be used
+     * @deprecated Use {@link #create(Context, String, MasterKey,
+     * PrefKeyEncryptionScheme, PrefValueEncryptionScheme)} instead.
+     */
+    @Deprecated
+    @NonNull
     public static SharedPreferences create(@NonNull String fileName,
             @NonNull String masterKeyAlias,
             @NonNull Context context,
@@ -114,14 +145,15 @@
             throws GeneralSecurityException, IOException {
         TinkConfig.register();
 
+        final Context applicationContext = context.getApplicationContext();
         KeysetHandle daeadKeysetHandle = new AndroidKeysetManager.Builder()
                 .withKeyTemplate(prefKeyEncryptionScheme.getKeyTemplate())
-                .withSharedPref(context, KEY_KEYSET_ALIAS, fileName)
+                .withSharedPref(applicationContext, KEY_KEYSET_ALIAS, fileName)
                 .withMasterKeyUri(KEYSTORE_PATH_URI + masterKeyAlias)
                 .build().getKeysetHandle();
         KeysetHandle aeadKeysetHandle = new AndroidKeysetManager.Builder()
                 .withKeyTemplate(prefValueEncryptionScheme.getKeyTemplate())
-                .withSharedPref(context, VALUE_KEYSET_ALIAS, fileName)
+                .withSharedPref(applicationContext, VALUE_KEYSET_ALIAS, fileName)
                 .withMasterKeyUri(KEYSTORE_PATH_URI + masterKeyAlias)
                 .build().getKeysetHandle();
 
@@ -129,7 +161,8 @@
         Aead aead = aeadKeysetHandle.getPrimitive(Aead.class);
 
         return new EncryptedSharedPreferences(fileName, masterKeyAlias,
-                context.getSharedPreferences(fileName, Context.MODE_PRIVATE), aead, daead);
+                applicationContext.getSharedPreferences(fileName, Context.MODE_PRIVATE), aead,
+                daead);
     }
 
     /**
diff --git a/security/crypto/src/main/java/androidx/security/crypto/MasterKey.java b/security/crypto/src/main/java/androidx/security/crypto/MasterKey.java
new file mode 100644
index 0000000..a3790ad
--- /dev/null
+++ b/security/crypto/src/main/java/androidx/security/crypto/MasterKey.java
@@ -0,0 +1,360 @@
+/*
+ * Copyright 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 androidx.security.crypto;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+
+import androidx.annotation.IntRange;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+
+/**
+ * Wrapper for a master key used in the library.
+ *
+ * On Android M (API 23) and above, this is class references a key that's stored in the
+ * Android Keystore. On Android L (API 21, 22), there isn't a master key.
+ */
+public final class MasterKey {
+    static final String KEYSTORE_PATH_URI = "android-keystore://";
+
+    /**
+     * The default master key alias.
+     */
+    public static final String DEFAULT_MASTER_KEY_ALIAS = "_androidx_security_master_key_";
+
+    /**
+     * The default and recommended size for the master key.
+     */
+    public static final int DEFAULT_AES_GCM_MASTER_KEY_SIZE = 256;
+
+    private static final int DEFAULT_AUTHENTICATION_VALIDITY_DURATION_SECONDS = 5 * 60;
+
+    @NonNull
+    private final String mKeyAlias;
+    @Nullable
+    private final KeyGenParameterSpec mKeyGenParameterSpec;
+
+    /**
+     * Algorithm/Cipher choices used for the master key.
+     */
+    public enum KeyScheme {
+        AES256_GCM
+    }
+
+    /**
+     * The default validity period for authentication in seconds.
+     */
+    @SuppressLint("MethodNameUnits")
+    public static int getDefaultAuthenticationValidityDurationSeconds() {
+        return DEFAULT_AUTHENTICATION_VALIDITY_DURATION_SECONDS;
+    }
+
+    /* package */ MasterKey(@NonNull String keyAlias, @Nullable Object keyGenParameterSpec) {
+        mKeyAlias = keyAlias;
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            mKeyGenParameterSpec = (KeyGenParameterSpec) keyGenParameterSpec;
+        } else {
+            mKeyGenParameterSpec = null;
+        }
+    }
+
+    /**
+     * Checks if this key is backed by the Android Keystore.
+     *
+     * @return {@code true} if the key is in Android Keystore, {@code false} otherwise. This
+     * method always returns false when called on Android Lollipop (API 21 and 22).
+     */
+    public boolean isKeyStoreBacked() {
+        // Keystore is not used prior to Android M (API 23)
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+            return false;
+        }
+
+        try {
+            KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+            keyStore.load(null);
+            return keyStore.containsAlias(mKeyAlias);
+        } catch (KeyStoreException | CertificateException
+                | NoSuchAlgorithmException | IOException ignored) {
+            return false;
+        }
+    }
+
+    /**
+     * Gets whether user authentication is required to use this key.
+     *
+     * This method always returns {@code false} on Android L (API 21 + 22).
+     */
+    public boolean isUserAuthenticationRequired() {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+            return false;
+        }
+        return mKeyGenParameterSpec != null && mKeyGenParameterSpec.isUserAuthenticationRequired();
+    }
+
+    /**
+     * Gets the duration in seconds that the key is unlocked for following user authentication.
+     *
+     * The value returned for this method is only meaningful on Android M+ (API 23) when
+     * {@link #isUserAuthenticationRequired()} returns {@code true}.
+     *
+     * @return The duration the key is unlocked for in seconds.
+     */
+    @SuppressLint("MethodNameUnits")
+    public int getUserAuthenticationValidityDurationSeconds() {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+            return 0;
+        }
+        return mKeyGenParameterSpec == null ? 0 :
+                mKeyGenParameterSpec.getUserAuthenticationValidityDurationSeconds();
+    }
+
+    /**
+     * Gets whether the key is backed by strong box.
+     */
+    public boolean isStrongBoxBacked() {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P || mKeyGenParameterSpec == null) {
+            return false;
+        }
+        return mKeyGenParameterSpec.isStrongBoxBacked();
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "MasterKey{keyAlias=" + mKeyAlias
+                + ", isKeyStoreBacked=" + isKeyStoreBacked()
+                + "}";
+    }
+
+    @NonNull/* package */ String getKeyAlias() {
+        return mKeyAlias;
+    }
+
+    /**
+     * Builder for generating a {@link MasterKey}.
+     */
+    public static final class Builder {
+        @NonNull
+        private final String mKeyAlias;
+
+        @Nullable
+        private KeyGenParameterSpec mKeyGenParameterSpec;
+        @Nullable
+        private KeyScheme mKeyScheme;
+
+        private boolean mAuthenticationRequired;
+        private int mUserAuthenticationValidityDurationSeconds;
+        private boolean mTryToUseStrongBox;
+
+        private final Context mContext;
+
+        /**
+         * Creates a builder for a {@link MasterKey} using the default alias of
+         * {@link #DEFAULT_MASTER_KEY_ALIAS}.
+         *
+         * @param context The context to use with this master key.
+         */
+        public Builder(@NonNull Context context) {
+            this(context, MasterKey.DEFAULT_MASTER_KEY_ALIAS);
+        }
+
+        /**
+         * Creates a builder for a {@link MasterKey}.
+         *
+         * @param context The context to use with this master key.
+         */
+        public Builder(@NonNull Context context, @NonNull String keyAlias) {
+            mContext = context.getApplicationContext();
+            mKeyAlias = keyAlias;
+        }
+
+        /**
+         * Sets a {@link KeyScheme} to be used for the master key.
+         * This uses a default {@link KeyGenParameterSpec} associated with the provided
+         * {@code KeyScheme}.
+         * NOTE: Either this method OR {@link #setKeyGenParameterSpec} should be used to set
+         * the parameters to use for building the master key. Calling either function after
+         * the other will throw an {@link IllegalArgumentException}.
+         *
+         * @param keyScheme The KeyScheme to use.
+         * @return This builder.
+         */
+        @NonNull
+        public Builder setKeyScheme(@NonNull KeyScheme keyScheme) {
+            switch (keyScheme) {
+                case AES256_GCM:
+                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                        if (mKeyGenParameterSpec != null) {
+                            throw new IllegalArgumentException("KeyScheme set after setting a "
+                                    + "KeyGenParamSpec");
+                        }
+                    }
+                    break;
+                default:
+                    throw new IllegalArgumentException("Unsupported scheme: " + keyScheme);
+            }
+            mKeyScheme = keyScheme;
+            return this;
+        }
+
+        /**
+         * When used with {@link #setKeyScheme(KeyScheme)}, sets that the built master key should
+         * require the user to authenticate before it's unlocked, probably using the
+         * androidx.biometric library.
+         *
+         * This method sets the validity duration of the key to
+         * {@link #getDefaultAuthenticationValidityDurationSeconds()}.
+         *
+         * @param authenticationRequired Whether user authentication should be required to use
+         *                               the key.
+         * @return This builder.
+         */
+        @NonNull
+        public Builder setUserAuthenticationRequired(boolean authenticationRequired) {
+            return setUserAuthenticationRequired(authenticationRequired,
+                    getDefaultAuthenticationValidityDurationSeconds());
+        }
+
+        /**
+         * When used with {@link #setKeyScheme(KeyScheme)}, sets that the built master key should
+         * require the user to authenticate before it's unlocked, probably using the
+         * androidx.biometric library, and that the key should remain unlocked for the provided
+         * duration.
+         *
+         * @param authenticationRequired                    Whether user authentication should be
+         *                                                  required to use the key.
+         * @param userAuthenticationValidityDurationSeconds Duration in seconds that the key
+         *                                                  should remain unlocked following user
+         *                                                  authentication.
+         * @return This builder.
+         */
+        @NonNull
+        public Builder setUserAuthenticationRequired(boolean authenticationRequired,
+                @IntRange(from = 1) int userAuthenticationValidityDurationSeconds) {
+            mAuthenticationRequired = authenticationRequired;
+            mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
+            return this;
+        }
+
+        /**
+         * Sets whether or not to try to make this key strong box backed. This setting is only
+         * applicable on {@link Build.VERSION_CODES#P} and above, and only on devices that
+         * support Strongbox.
+         *
+         * @param tryToUseStrongBox Whether to try and use strongbox
+         * @return This builder.
+         */
+        @NonNull
+        public Builder setTryToUseStrongBox(boolean tryToUseStrongBox) {
+            mTryToUseStrongBox = tryToUseStrongBox;
+            return this;
+        }
+
+        /**
+         * Sets a custom {@link KeyGenParameterSpec} to use as the basis of the master key.
+         * NOTE: Either this method OR {@link #setKeyGenParameterSpec} should be used to set
+         * the parameters to use for building the master key. Calling either function after
+         * the other will throw an {@link IllegalArgumentException}.
+         *
+         * @param keyGenParameterSpec The key spec to use.
+         * @return This builder.
+         */
+        @NonNull
+        @RequiresApi(Build.VERSION_CODES.M)
+        public Builder setKeyGenParameterSpec(@NonNull KeyGenParameterSpec keyGenParameterSpec) {
+            if (mKeyScheme != null) {
+                throw new IllegalArgumentException("KeyGenParamSpec set after setting a "
+                        + "KeyScheme");
+            }
+            if (!mKeyAlias.equals(keyGenParameterSpec.getKeystoreAlias())) {
+                throw new IllegalArgumentException("KeyGenParamSpec's key alias does not match "
+                        + "provided alias (" + mKeyAlias + " vs "
+                        + keyGenParameterSpec.getKeystoreAlias());
+            }
+            mKeyGenParameterSpec = keyGenParameterSpec;
+            return this;
+        }
+
+        /**
+         * Builds a {@link MasterKey} from this builder.
+         *
+         * @return The master key.
+         */
+        @NonNull
+        public MasterKey build() throws GeneralSecurityException, IOException {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                return buildOnM();
+            } else {
+                return new MasterKey(mKeyAlias, null);
+            }
+        }
+
+        @RequiresApi(Build.VERSION_CODES.M)
+        private MasterKey buildOnM() throws GeneralSecurityException, IOException {
+            if (mKeyScheme == null && mKeyGenParameterSpec == null) {
+                throw new IllegalArgumentException("build() called before "
+                        + "setKeyGenParameterSpec or setKeyScheme.");
+            }
+
+            if (mKeyScheme == KeyScheme.AES256_GCM) {
+                KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(
+                        mKeyAlias,
+                        KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+                        .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
+                        .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+                        .setKeySize(DEFAULT_AES_GCM_MASTER_KEY_SIZE);
+
+                if (mAuthenticationRequired) {
+                    builder.setUserAuthenticationRequired(true)
+                            .setUserAuthenticationValidityDurationSeconds(
+                                    mUserAuthenticationValidityDurationSeconds);
+                }
+
+                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && mTryToUseStrongBox) {
+                    if (mContext.getPackageManager().hasSystemFeature(
+                            PackageManager.FEATURE_STRONGBOX_KEYSTORE)) {
+                        builder.setIsStrongBoxBacked(true);
+                    }
+                }
+
+                mKeyGenParameterSpec = builder.build();
+            }
+            if (mKeyGenParameterSpec == null) {
+                // This really should not happen.
+                throw new NullPointerException("KeyGenParameterSpec was null after build() check");
+            }
+
+            @SuppressWarnings("deprecation")
+            String keyAlias = MasterKeys.getOrCreate(mKeyGenParameterSpec);
+            return new MasterKey(keyAlias, mKeyGenParameterSpec);
+        }
+    }
+}
diff --git a/security/crypto/src/main/java/androidx/security/crypto/MasterKeys.java b/security/crypto/src/main/java/androidx/security/crypto/MasterKeys.java
index 289a440..2590053 100644
--- a/security/crypto/src/main/java/androidx/security/crypto/MasterKeys.java
+++ b/security/crypto/src/main/java/androidx/security/crypto/MasterKeys.java
@@ -16,15 +16,18 @@
 
 package androidx.security.crypto;
 
+import android.os.Build;
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
 import androidx.annotation.VisibleForTesting;
 
 import java.io.IOException;
 import java.security.GeneralSecurityException;
 import java.security.KeyStore;
+import java.security.ProviderException;
 import java.util.Arrays;
 
 import javax.crypto.KeyGenerator;
@@ -33,18 +36,21 @@
  * Convenient methods to create and obtain master keys in Android Keystore.
  *
  * <p>The master keys are used to encrypt data encryption keys for encrypting files and preferences.
+ *
+ * @deprecated Use {@link MasterKey.Builder} to work with master keys.
  */
+@Deprecated
 public final class MasterKeys {
     private MasterKeys() {
     }
 
-    private static final int KEY_SIZE = 256;
+    static final String MASTER_KEY_ALIAS = MasterKey.DEFAULT_MASTER_KEY_ALIAS;
+    static final int KEY_SIZE = MasterKey.DEFAULT_AES_GCM_MASTER_KEY_SIZE;
 
     private static final String ANDROID_KEYSTORE = "AndroidKeyStore";
-    static final String KEYSTORE_PATH_URI = "android-keystore://";
-    static final String MASTER_KEY_ALIAS = "_androidx_security_master_key_";
 
     @NonNull
+    @RequiresApi(Build.VERSION_CODES.M)
     public static final KeyGenParameterSpec AES256_GCM_SPEC =
             createAES256GCMKeyGenParameterSpec(MASTER_KEY_ALIAS);
 
@@ -59,6 +65,8 @@
      * @return The spec for the master key with the specified keyAlias
      */
     @NonNull
+    @RequiresApi(Build.VERSION_CODES.M)
+    @SuppressWarnings("SameParameterValue")
     private static KeyGenParameterSpec createAES256GCMKeyGenParameterSpec(
             @NonNull String keyAlias) {
         KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(
@@ -80,6 +88,7 @@
      * @return The key alias for the master key
      */
     @NonNull
+    @RequiresApi(Build.VERSION_CODES.M)
     public static String getOrCreate(
             @NonNull KeyGenParameterSpec keyGenParameterSpec)
             throws GeneralSecurityException, IOException {
@@ -91,6 +100,7 @@
     }
 
     @VisibleForTesting
+    @RequiresApi(Build.VERSION_CODES.M)
     static void validate(KeyGenParameterSpec spec) {
         if (spec.getKeySize() != KEY_SIZE) {
             throw new IllegalArgumentException(
@@ -121,13 +131,20 @@
         }
     }
 
+    @RequiresApi(Build.VERSION_CODES.M)
     private static void generateKey(@NonNull KeyGenParameterSpec keyGenParameterSpec)
             throws GeneralSecurityException {
-        KeyGenerator keyGenerator = KeyGenerator.getInstance(
-                KeyProperties.KEY_ALGORITHM_AES,
-                ANDROID_KEYSTORE);
-        keyGenerator.init(keyGenParameterSpec);
-        keyGenerator.generateKey();
+        try {
+            KeyGenerator keyGenerator = KeyGenerator.getInstance(
+                    KeyProperties.KEY_ALGORITHM_AES,
+                    ANDROID_KEYSTORE);
+            keyGenerator.init(keyGenParameterSpec);
+            keyGenerator.generateKey();
+        } catch (ProviderException providerException) {
+            // Android 10 (API 29) throws a ProviderException under certain circumstances. Wrap
+            // that as a GeneralSecurityException so it's more consistent across API levels.
+            throw new GeneralSecurityException(providerException.getMessage(), providerException);
+        }
     }
 
     private static boolean keyExists(@NonNull String keyAlias)