KeyChain: Implement user-selectability

Store indication of whether each key in KeyChain can be selected by
users from the UI, or restricted for selection by the DPC only.

This CL contains the implementation of storing the information
in a SQLite table, a fallow-up CL will wire it to the AliasLoader
and KeyChainService.

Bug: 65624467
Test: New robolectric tests (run manually)
Change-Id: I5c51f4b5501ceccf070e7843864c10a0813509b3
diff --git a/robotests/src/com/android/keychain/internal/GrantsDatabaseTest.java b/robotests/src/com/android/keychain/internal/GrantsDatabaseTest.java
index 074b376..9dd5946 100644
--- a/robotests/src/com/android/keychain/internal/GrantsDatabaseTest.java
+++ b/robotests/src/com/android/keychain/internal/GrantsDatabaseTest.java
@@ -94,4 +94,21 @@
         Assert.assertFalse(mGrantsDB.hasGrant(DUMMY_UID2, DUMMY_ALIAS));
         Assert.assertFalse(mGrantsDB.hasGrant(DUMMY_UID, DUMMY_ALIAS2));
     }
+
+    @Test
+    public void testIsUserSelectable() {
+        Assert.assertFalse(mGrantsDB.isUserSelectable(DUMMY_ALIAS));
+        mGrantsDB.setIsUserSelectable(DUMMY_ALIAS, true);
+        Assert.assertTrue(mGrantsDB.isUserSelectable(DUMMY_ALIAS));
+    }
+
+    @Test
+    public void testSetUserSelectable() {
+        mGrantsDB.setIsUserSelectable(DUMMY_ALIAS, true);
+        Assert.assertTrue(mGrantsDB.isUserSelectable(DUMMY_ALIAS));
+        mGrantsDB.setIsUserSelectable(DUMMY_ALIAS, false);
+        Assert.assertFalse(mGrantsDB.isUserSelectable(DUMMY_ALIAS));
+        mGrantsDB.setIsUserSelectable(DUMMY_ALIAS, true);
+        Assert.assertTrue(mGrantsDB.isUserSelectable(DUMMY_ALIAS));
+    }
 }
diff --git a/src/com/android/keychain/internal/GrantsDatabase.java b/src/com/android/keychain/internal/GrantsDatabase.java
index 591d0a5..4783378 100644
--- a/src/com/android/keychain/internal/GrantsDatabase.java
+++ b/src/com/android/keychain/internal/GrantsDatabase.java
@@ -50,6 +50,11 @@
 
     private static final String SELECTION_GRANTS_BY_ALIAS = GRANTS_ALIAS + "=?";
 
+    private static final String TABLE_SELECTABLE = "userselectable";
+    private static final String SELECTABLE_IS_SELECTABLE = "is_selectable";
+    private static final String COUNT_SELECTABILITY_FOR_ALIAS =
+            "SELECT COUNT(*) FROM " + TABLE_SELECTABLE + " WHERE " + GRANTS_ALIAS + "=?";
+
     public DatabaseHelper mDatabaseHelper;
 
     private class DatabaseHelper extends SQLiteOpenHelper {
@@ -72,6 +77,18 @@
                             + ","
                             + GRANTS_GRANTEE_UID
                             + "))");
+
+            db.execSQL(
+                    "CREATE TABLE "
+                            + TABLE_SELECTABLE
+                            + " (  "
+                            + GRANTS_ALIAS
+                            + " STRING NOT NULL,  "
+                            + SELECTABLE_IS_SELECTABLE
+                            + " STRING NOT NULL,  "
+                            + "UNIQUE ("
+                            + GRANTS_ALIAS
+                            + "))");
         }
 
         @Override
@@ -171,4 +188,37 @@
             db.endTransaction();
         }
     }
+
+    public void setIsUserSelectable(final String alias, final boolean userSelectable) {
+        final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
+        final ContentValues values = new ContentValues();
+        values.put(GRANTS_ALIAS, alias);
+        values.put(SELECTABLE_IS_SELECTABLE, Boolean.toString(userSelectable));
+
+        db.replace(TABLE_SELECTABLE, null, values);
+    }
+
+    public boolean isUserSelectable(final String alias) {
+        final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
+        try (Cursor res =
+                db.query(
+                        TABLE_SELECTABLE,
+                        new String[] {SELECTABLE_IS_SELECTABLE},
+                        SELECTION_GRANTS_BY_ALIAS,
+                        new String[] {alias},
+                        null /* group by */,
+                        null /* having */,
+                        null /* order by */)) {
+            if (res == null || !res.moveToNext()) {
+                return false;
+            }
+
+            boolean isSelectable = Boolean.parseBoolean(res.getString(0));
+            if (!res.isAfterLast()) {
+                // BUG! Should not have more than one result for any given alias.
+                Log.w(TAG, String.format("Have more than one result for alias %s", alias));
+            }
+            return isSelectable;
+        }
+    }
 }