+ @VisibleForTesting
+ protected IWeaver getWeaverService() throws RemoteException {
+ try {
+ return IWeaver.getService();
+ } catch (NoSuchElementException e) {
+ Slog.i(TAG, "Device does not support weaver");
+ return null;
+ }
+ }
+
+ public synchronized void initWeaverService() {
+ if (mWeaver != null) {
+ return;
+ }
+ try {
+ mWeaverConfig = null;
+ mWeaver = getWeaverService();
+ if (mWeaver != null) {
+ mWeaver.getConfig((int status, WeaverConfig config) -> {
+ if (status == WeaverStatus.OK && config.slots > 0) {
+ mWeaverConfig = config;
+ } else {
+ Slog.e(TAG, "Failed to get weaver config, status " + status
+ + " slots: " + config.slots);
+ mWeaver = null;
+ }
+ });
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get weaver service", e);
+ }
+ }
+
+ private synchronized boolean isWeaverAvailable() {
+ if (mWeaver == null) {
+ //Re-initializing weaver in case there was a transient error preventing access to it.
+ initWeaverService();
+ }
+ return mWeaver != null && mWeaverConfig.slots > 0;
+ }
+
+ /**
+ * Enroll the given key value pair into the specified weaver slot. if the given key is null,
+ * a default all-zero key is used. If the value is not specified, a fresh random secret is
+ * generated as the value.
+ *
+ * @return the value stored in the weaver slot
+ * @throws RemoteException
+ */
+ private byte[] weaverEnroll(int slot, byte[] key, @Nullable byte[] value)
+ throws RemoteException {
+ if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) {
+ throw new RuntimeException("Invalid slot for weaver");
+ }
+ if (key == null) {
+ key = new byte[mWeaverConfig.keySize];
+ } else if (key.length != mWeaverConfig.keySize) {
+ throw new RuntimeException("Invalid key size for weaver");
+ }
+ if (value == null) {
+ value = secureRandom(mWeaverConfig.valueSize);
+ }
+ int writeStatus = mWeaver.write(slot, toByteArrayList(key), toByteArrayList(value));
+ if (writeStatus != WeaverStatus.OK) {
+ Log.e(TAG, "weaver write failed, slot: " + slot + " status: " + writeStatus);
+ return null;
+ }
+ return value;
+ }
+
+ /**
+ * Verify the supplied key against a weaver slot, returning a response indicating whether
+ * the verification is successful, throttled or failed. If successful, the bound secret
+ * is also returned.
+ * @throws RemoteException
+ */
+ private VerifyCredentialResponse weaverVerify(int slot, byte[] key) throws RemoteException {
+ if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) {
+ throw new RuntimeException("Invalid slot for weaver");
+ }
+ if (key == null) {
+ key = new byte[mWeaverConfig.keySize];
+ } else if (key.length != mWeaverConfig.keySize) {
+ throw new RuntimeException("Invalid key size for weaver");
+ }
+ final VerifyCredentialResponse[] response = new VerifyCredentialResponse[1];
+ mWeaver.read(slot, toByteArrayList(key), (int status, WeaverReadResponse readResponse) -> {
+ switch (status) {
+ case WeaverReadStatus.OK:
+ response[0] = new VerifyCredentialResponse(
+ fromByteArrayList(readResponse.value));
+ break;
+ case WeaverReadStatus.THROTTLE:
+ response[0] = new VerifyCredentialResponse(readResponse.timeout);
+ Log.e(TAG, "weaver read failed (THROTTLE), slot: " + slot);
+ break;
+ case WeaverReadStatus.INCORRECT_KEY:
+ if (readResponse.timeout == 0) {
+ response[0] = VerifyCredentialResponse.ERROR;
+ Log.e(TAG, "weaver read failed (INCORRECT_KEY), slot: " + slot);
+ } else {
+ response[0] = new VerifyCredentialResponse(readResponse.timeout);
+ Log.e(TAG, "weaver read failed (INCORRECT_KEY/THROTTLE), slot: " + slot);
+ }
+ break;
+ case WeaverReadStatus.FAILED:
+ response[0] = VerifyCredentialResponse.ERROR;
+ Log.e(TAG, "weaver read failed (FAILED), slot: " + slot);
+ break;
+ default:
+ response[0] = VerifyCredentialResponse.ERROR;
+ Log.e(TAG, "weaver read unknown status " + status + ", slot: " + slot);
+ break;
+ }
+ });
+ return response[0];
+ }
+
+ public void removeUser(int userId) {
+ if (isWeaverAvailable()) {
+ for (long handle : mStorage.listSyntheticPasswordHandlesForUser(WEAVER_SLOT_NAME,
+ userId)) {
+ destroyWeaverSlot(handle, userId);
+ }
+ }
+ }