Merge "locksettings: Add AIDL weaver HAL support"
This commit is contained in:
@@ -161,6 +161,7 @@ java_library_static {
|
||||
"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",
|
||||
|
||||
@@ -24,13 +24,14 @@ import android.annotation.Nullable;
|
||||
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.lang.annotation.RetentionPolicy;
|
||||
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 @@ public class SyntheticPasswordManager {
|
||||
}
|
||||
|
||||
@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,18 +555,30 @@ public class SyntheticPasswordManager {
|
||||
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
|
||||
@@ -551,46 +593,39 @@ public class SyntheticPasswordManager {
|
||||
} 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
return response[0];
|
||||
}
|
||||
|
||||
public void removeUser(IGateKeeperService gatekeeper, int userId) {
|
||||
@@ -1660,22 +1695,6 @@ public class SyntheticPasswordManager {
|
||||
|
||||
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();
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -113,7 +113,7 @@ public class MockSyntheticPasswordManager extends SyntheticPasswordManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IWeaver getWeaverService() throws RemoteException {
|
||||
protected IWeaver getWeaverHidlService() throws RemoteException {
|
||||
return mWeaverService;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user