locksettings: Add AIDL weaver HAL support
Test: atest FrameworksServicesTests:com.android.server.locksettings.LockSettingsServiceTests
Test: Set PIN, enter incorrect PIN, enter correct PIN
Test: Remove HIDL HAL service and flash new image
Test: enter incorrect PIN, enter correct PIN.
Test: Set a new PIN and retest all without HIDL service
Bug: 182976659
Change-Id: Icf720b5ab4e93675cf72ab441fd4af86a46f90e9
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 1e1d610..5c00452 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -161,6 +161,7 @@
"android.hardware.tv.cec-V1-java",
"android.hardware.tv.hdmi-V1-java",
"android.hardware.weaver-V1.0-java",
+ "android.hardware.weaver-V2-java",
"android.hardware.biometrics.face-V1.0-java",
"android.hardware.biometrics.fingerprint-V2.3-java",
"android.hardware.oemlock-V1.0-java",
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index acd7cc1..66ce429 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -24,13 +24,14 @@
import android.app.admin.PasswordMetrics;
import android.content.Context;
import android.content.pm.UserInfo;
-import android.hardware.weaver.V1_0.IWeaver;
-import android.hardware.weaver.V1_0.WeaverConfig;
-import android.hardware.weaver.V1_0.WeaverReadResponse;
-import android.hardware.weaver.V1_0.WeaverReadStatus;
-import android.hardware.weaver.V1_0.WeaverStatus;
+import android.hardware.weaver.IWeaver;
+import android.hardware.weaver.WeaverConfig;
+import android.hardware.weaver.WeaverReadResponse;
+import android.hardware.weaver.WeaverReadStatus;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
import android.os.UserManager;
import android.provider.Settings;
import android.security.GateKeeper;
@@ -60,7 +61,6 @@
import java.nio.ByteBuffer;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
@@ -464,37 +464,67 @@
}
@VisibleForTesting
- protected IWeaver getWeaverService() throws RemoteException {
+ protected android.hardware.weaver.V1_0.IWeaver getWeaverHidlService() throws RemoteException {
try {
- return IWeaver.getService(/* retry */ true);
+ return android.hardware.weaver.V1_0.IWeaver.getService(/* retry */ true);
} catch (NoSuchElementException e) {
- Slog.i(TAG, "Device does not support weaver");
return null;
}
}
+ private IWeaver getWeaverService() {
+ // Try to get the AIDL service first
+ try {
+ IWeaver aidlWeaver = IWeaver.Stub.asInterface(
+ ServiceManager.waitForDeclaredService(IWeaver.DESCRIPTOR + "/default"));
+ if (aidlWeaver != null) {
+ Slog.i(TAG, "Using AIDL weaver service");
+ return aidlWeaver;
+ }
+ } catch (SecurityException e) {
+ Slog.w(TAG, "Does not have permissions to get AIDL weaver service");
+ }
+
+ // If the AIDL service can't be found, look for the HIDL service
+ try {
+ android.hardware.weaver.V1_0.IWeaver hidlWeaver = getWeaverHidlService();
+ if (hidlWeaver != null) {
+ Slog.i(TAG, "Using HIDL weaver service");
+ return new WeaverHidlWrapper(hidlWeaver);
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to get HIDL weaver service.", e);
+ }
+ Slog.w(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;
- }
- });
- mPasswordSlotManager.refreshActiveSlots(getUsedWeaverSlots());
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to get weaver service", e);
+
+ IWeaver weaver = getWeaverService();
+ if (weaver == null) {
+ return;
}
+
+ // Get the config
+ WeaverConfig weaverConfig = null;
+ try {
+ weaverConfig = weaver.getConfig();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get weaver config", e);
+ }
+ if (weaverConfig == null || weaverConfig.slots <= 0) {
+ Slog.e(TAG, "Failed to initialize weaver config");
+ return;
+ }
+
+ mWeaver = weaver;
+ mWeaverConfig = weaverConfig;
+ mPasswordSlotManager.refreshActiveSlots(getUsedWeaverSlots());
+ Slog.i(TAG, "Weaver service initialized");
}
private synchronized boolean isWeaverAvailable() {
@@ -525,19 +555,31 @@
value = secureRandom(mWeaverConfig.valueSize);
}
try {
- int writeStatus = mWeaver.write(slot, toByteArrayList(key), toByteArrayList(value));
- if (writeStatus != WeaverStatus.OK) {
- Slog.e(TAG, "weaver write failed, slot: " + slot + " status: " + writeStatus);
- return null;
- }
+ mWeaver.write(slot, key, value);
} catch (RemoteException e) {
- Slog.e(TAG, "weaver write failed", e);
+ Slog.e(TAG, "weaver write binder call failed, slot: " + slot, e);
+ return null;
+ } catch (ServiceSpecificException e) {
+ Slog.e(TAG, "weaver write failed, slot: " + slot, e);
return null;
}
return value;
}
/**
+ * Create a VerifyCredentialResponse from a timeout base on the WeaverReadResponse.
+ * This checks the received timeout(long) to make sure it sure it fits in an int before
+ * using it. If it doesn't fit, we use Integer.MAX_VALUE.
+ */
+ private static VerifyCredentialResponse responseFromTimeout(WeaverReadResponse response) {
+ int timeout =
+ response.timeout > Integer.MAX_VALUE || response.timeout < 0
+ ? Integer.MAX_VALUE
+ : (int) response.timeout;
+ return VerifyCredentialResponse.fromTimeout(timeout);
+ }
+
+ /**
* 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.
@@ -551,46 +593,39 @@
} else if (key.length != mWeaverConfig.keySize) {
throw new IllegalArgumentException("Invalid key size for weaver");
}
- final VerifyCredentialResponse[] response = new VerifyCredentialResponse[1];
+ final WeaverReadResponse readResponse;
try {
- mWeaver.read(slot, toByteArrayList(key),
- (int status, WeaverReadResponse readResponse) -> {
- switch (status) {
- case WeaverReadStatus.OK:
- response[0] = new VerifyCredentialResponse.Builder().setGatekeeperHAT(
- fromByteArrayList(readResponse.value)).build();
- break;
- case WeaverReadStatus.THROTTLE:
- response[0] = VerifyCredentialResponse
- .fromTimeout(readResponse.timeout);
- Slog.e(TAG, "weaver read failed (THROTTLE), slot: " + slot);
- break;
- case WeaverReadStatus.INCORRECT_KEY:
- if (readResponse.timeout == 0) {
- response[0] = VerifyCredentialResponse.ERROR;
- Slog.e(TAG, "weaver read failed (INCORRECT_KEY), slot: " + slot);
- } else {
- response[0] = VerifyCredentialResponse
- .fromTimeout(readResponse.timeout);
- Slog.e(TAG, "weaver read failed (INCORRECT_KEY/THROTTLE), slot: "
- + slot);
- }
- break;
- case WeaverReadStatus.FAILED:
- response[0] = VerifyCredentialResponse.ERROR;
- Slog.e(TAG, "weaver read failed (FAILED), slot: " + slot);
- break;
- default:
- response[0] = VerifyCredentialResponse.ERROR;
- Slog.e(TAG, "weaver read unknown status " + status + ", slot: " + slot);
- break;
- }
- });
+ readResponse = mWeaver.read(slot, key);
} catch (RemoteException e) {
- response[0] = VerifyCredentialResponse.ERROR;
Slog.e(TAG, "weaver read failed, slot: " + slot, e);
+ return VerifyCredentialResponse.ERROR;
}
- return response[0];
+
+ switch (readResponse.status) {
+ case WeaverReadStatus.OK:
+ return new VerifyCredentialResponse.Builder()
+ .setGatekeeperHAT(readResponse.value)
+ .build();
+ case WeaverReadStatus.THROTTLE:
+ Slog.e(TAG, "weaver read failed (THROTTLE), slot: " + slot);
+ return responseFromTimeout(readResponse);
+ case WeaverReadStatus.INCORRECT_KEY:
+ if (readResponse.timeout == 0) {
+ Slog.e(TAG, "weaver read failed (INCORRECT_KEY), slot: " + slot);
+ return VerifyCredentialResponse.ERROR;
+ } else {
+ Slog.e(TAG, "weaver read failed (INCORRECT_KEY/THROTTLE), slot: " + slot);
+ return responseFromTimeout(readResponse);
+ }
+ case WeaverReadStatus.FAILED:
+ Slog.e(TAG, "weaver read failed (FAILED), slot: " + slot);
+ return VerifyCredentialResponse.ERROR;
+ default:
+ Slog.e(TAG,
+ "weaver read unknown status " + readResponse.status
+ + ", slot: " + slot);
+ return VerifyCredentialResponse.ERROR;
+ }
}
public void removeUser(IGateKeeperService gatekeeper, int userId) {
@@ -1660,22 +1695,6 @@
native long nativeSidFromPasswordHandle(byte[] handle);
- protected static ArrayList<Byte> toByteArrayList(byte[] data) {
- ArrayList<Byte> result = new ArrayList<Byte>(data.length);
- for (int i = 0; i < data.length; i++) {
- result.add(data[i]);
- }
- return result;
- }
-
- protected static byte[] fromByteArrayList(ArrayList<Byte> data) {
- byte[] result = new byte[data.size()];
- for (int i = 0; i < data.size(); i++) {
- result[i] = data.get(i);
- }
- return result;
- }
-
@VisibleForTesting
static byte[] bytesToHex(byte[] bytes) {
return HexEncoding.encodeToString(bytes).getBytes();
diff --git a/services/core/java/com/android/server/locksettings/WeaverHidlWrapper.java b/services/core/java/com/android/server/locksettings/WeaverHidlWrapper.java
new file mode 100644
index 0000000..9d93c3d
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/WeaverHidlWrapper.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.locksettings;
+
+import android.hardware.weaver.V1_0.IWeaver;
+import android.hardware.weaver.V1_0.WeaverConfig;
+import android.hardware.weaver.V1_0.WeaverReadResponse;
+import android.hardware.weaver.V1_0.WeaverReadStatus;
+import android.hardware.weaver.V1_0.WeaverStatus;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.util.Slog;
+
+import java.util.ArrayList;
+
+/**
+ * Implement the AIDL IWeaver interface wrapping the HIDL implementation
+ */
+class WeaverHidlWrapper implements android.hardware.weaver.IWeaver {
+ private static final String TAG = "WeaverHidlWrapper";
+ private final IWeaver mImpl;
+
+ WeaverHidlWrapper(IWeaver impl) {
+ mImpl = impl;
+ }
+
+ private static ArrayList<Byte> toByteArrayList(byte[] data) {
+ ArrayList<Byte> result = new ArrayList<Byte>(data.length);
+ for (int i = 0; i < data.length; i++) {
+ result.add(data[i]);
+ }
+ return result;
+ }
+
+ private static byte[] fromByteArrayList(ArrayList<Byte> data) {
+ byte[] result = new byte[data.size()];
+ for (int i = 0; i < data.size(); i++) {
+ result[i] = data.get(i);
+ }
+ return result;
+ }
+
+ @Override
+ public String getInterfaceHash() {
+ // We do not require the interface hash as the client.
+ throw new UnsupportedOperationException(
+ "WeaverHidlWrapper does not support getInterfaceHash");
+ }
+ @Override
+ public int getInterfaceVersion() {
+ // Supports only V2 which is at feature parity.
+ return 2;
+ }
+ @Override
+ public android.os.IBinder asBinder() {
+ // There is no IHwBinder to IBinder. Not required as the client.
+ throw new UnsupportedOperationException("WeaverHidlWrapper does not support asBinder");
+ }
+
+ @Override
+ public android.hardware.weaver.WeaverConfig getConfig() throws RemoteException {
+ final WeaverConfig[] res = new WeaverConfig[1];
+ mImpl.getConfig((int status, WeaverConfig config) -> {
+ if (status == WeaverStatus.OK && config.slots > 0) {
+ res[0] = config;
+ } else {
+ res[0] = null;
+ Slog.e(TAG,
+ "Failed to get HIDL weaver config. status: " + status
+ + ", slots: " + config.slots);
+ }
+ });
+
+ if (res[0] == null) {
+ return null;
+ }
+ android.hardware.weaver.WeaverConfig config = new android.hardware.weaver.WeaverConfig();
+ config.slots = res[0].slots;
+ config.keySize = res[0].keySize;
+ config.valueSize = res[0].valueSize;
+ return config;
+ }
+
+ @Override
+ public android.hardware.weaver.WeaverReadResponse read(int slotId, byte[] key)
+ throws RemoteException {
+ final WeaverReadResponse[] res = new WeaverReadResponse[1];
+ final int[] status = new int[1];
+ mImpl.read(
+ slotId, toByteArrayList(key), (int inStatus, WeaverReadResponse readResponse) -> {
+ status[0] = inStatus;
+ res[0] = readResponse;
+ });
+
+ android.hardware.weaver.WeaverReadResponse aidlRes =
+ new android.hardware.weaver.WeaverReadResponse();
+ switch (status[0]) {
+ case WeaverReadStatus.OK:
+ aidlRes.status = android.hardware.weaver.WeaverReadStatus.OK;
+ break;
+ case WeaverReadStatus.THROTTLE:
+ aidlRes.status = android.hardware.weaver.WeaverReadStatus.THROTTLE;
+ break;
+ case WeaverReadStatus.INCORRECT_KEY:
+ aidlRes.status = android.hardware.weaver.WeaverReadStatus.INCORRECT_KEY;
+ break;
+ case WeaverReadStatus.FAILED:
+ aidlRes.status = android.hardware.weaver.WeaverReadStatus.FAILED;
+ break;
+ default:
+ aidlRes.status = android.hardware.weaver.WeaverReadStatus.FAILED;
+ break;
+ }
+ if (res[0] != null) {
+ aidlRes.timeout = res[0].timeout;
+ aidlRes.value = fromByteArrayList(res[0].value);
+ }
+ return aidlRes;
+ }
+
+ @Override
+ public void write(int slotId, byte[] key, byte[] value) throws RemoteException {
+ int writeStatus = mImpl.write(slotId, toByteArrayList(key), toByteArrayList(value));
+ if (writeStatus != WeaverStatus.OK) {
+ throw new ServiceSpecificException(
+ android.hardware.weaver.IWeaver.STATUS_FAILED, "Failed IWeaver.write call");
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java b/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java
index 8cb18a8..a40cb06 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java
@@ -113,7 +113,7 @@
}
@Override
- protected IWeaver getWeaverService() throws RemoteException {
+ protected IWeaver getWeaverHidlService() throws RemoteException {
return mWeaverService;
}