Merge "Revert "[CDM Transport] Exchange platform info to decide which Transport to"" into udc-dev am: 8186b7edc8
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21661331 Change-Id: Icb9a270ba798f7cbb9aa43410af17c7c179da747 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -23,7 +23,6 @@ import static android.companion.CompanionDeviceManager.COMPANION_DEVICE_DISCOVER
|
|||||||
import static android.content.ComponentName.createRelative;
|
import static android.content.ComponentName.createRelative;
|
||||||
|
|
||||||
import static com.android.server.companion.Utils.prepareForIpc;
|
import static com.android.server.companion.Utils.prepareForIpc;
|
||||||
import static com.android.server.companion.transport.Transport.MESSAGE_REQUEST_PERMISSION_RESTORE;
|
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
@@ -92,8 +91,7 @@ public class SystemDataTransferProcessor {
|
|||||||
mAssociationStore = associationStore;
|
mAssociationStore = associationStore;
|
||||||
mSystemDataTransferRequestStore = systemDataTransferRequestStore;
|
mSystemDataTransferRequestStore = systemDataTransferRequestStore;
|
||||||
mTransportManager = transportManager;
|
mTransportManager = transportManager;
|
||||||
mTransportManager.addListener(MESSAGE_REQUEST_PERMISSION_RESTORE,
|
mTransportManager.setListener(this::onReceivePermissionRestore);
|
||||||
this::onReceivePermissionRestore);
|
|
||||||
mPermissionControllerManager = mContext.getSystemService(PermissionControllerManager.class);
|
mPermissionControllerManager = mContext.getSystemService(PermissionControllerManager.class);
|
||||||
mExecutor = Executors.newSingleThreadExecutor();
|
mExecutor = Executors.newSingleThreadExecutor();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ import java.util.function.BiConsumer;
|
|||||||
/**
|
/**
|
||||||
* Helper class to perform attestation verification synchronously.
|
* Helper class to perform attestation verification synchronously.
|
||||||
*/
|
*/
|
||||||
public class AttestationVerifier {
|
class AttestationVerifier {
|
||||||
private static final long ATTESTATION_VERIFICATION_TIMEOUT_SECONDS = 10; // 10 seconds
|
private static final long ATTESTATION_VERIFICATION_TIMEOUT_SECONDS = 10; // 10 seconds
|
||||||
private static final String PARAM_OWNED_BY_SYSTEM = "android.key_owned_by_system";
|
private static final String PARAM_OWNED_BY_SYSTEM = "android.key_owned_by_system";
|
||||||
|
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ public class SecureChannel {
|
|||||||
this(in, out, callback, null, new AttestationVerifier(context));
|
this(in, out, callback, null, new AttestationVerifier(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
public SecureChannel(
|
private SecureChannel(
|
||||||
final InputStream in,
|
final InputStream in,
|
||||||
final OutputStream out,
|
final OutputStream out,
|
||||||
Callback callback,
|
Callback callback,
|
||||||
@@ -381,10 +381,9 @@ public class SecureChannel {
|
|||||||
|
|
||||||
private void exchangeAuthentication()
|
private void exchangeAuthentication()
|
||||||
throws IOException, GeneralSecurityException, BadHandleException, CryptoException {
|
throws IOException, GeneralSecurityException, BadHandleException, CryptoException {
|
||||||
if (mPreSharedKey != null) {
|
if (mVerifier == null) {
|
||||||
exchangePreSharedKey();
|
exchangePreSharedKey();
|
||||||
}
|
} else {
|
||||||
if (mVerifier != null) {
|
|
||||||
exchangeAttestation();
|
exchangeAttestation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,9 +19,9 @@ package com.android.server.companion.transport;
|
|||||||
import static android.Manifest.permission.DELIVER_COMPANION_MESSAGES;
|
import static android.Manifest.permission.DELIVER_COMPANION_MESSAGES;
|
||||||
|
|
||||||
import static com.android.server.companion.transport.Transport.MESSAGE_REQUEST_PERMISSION_RESTORE;
|
import static com.android.server.companion.transport.Transport.MESSAGE_REQUEST_PERMISSION_RESTORE;
|
||||||
import static com.android.server.companion.transport.Transport.MESSAGE_REQUEST_PLATFORM_INFO;
|
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.Nullable;
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.ActivityManagerInternal;
|
import android.app.ActivityManagerInternal;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -30,17 +30,12 @@ import android.content.pm.PackageManager.NameNotFoundException;
|
|||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.util.Slog;
|
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
|
|
||||||
import com.android.internal.annotations.GuardedBy;
|
import com.android.internal.annotations.GuardedBy;
|
||||||
import com.android.server.LocalServices;
|
import com.android.server.LocalServices;
|
||||||
import com.android.server.companion.transport.Transport.Listener;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
@@ -49,8 +44,6 @@ public class CompanionTransportManager {
|
|||||||
private static final String TAG = "CDM_CompanionTransportManager";
|
private static final String TAG = "CDM_CompanionTransportManager";
|
||||||
private static final boolean DEBUG = false;
|
private static final boolean DEBUG = false;
|
||||||
|
|
||||||
private static final int NON_ANDROID = -1;
|
|
||||||
|
|
||||||
private boolean mSecureTransportEnabled = true;
|
private boolean mSecureTransportEnabled = true;
|
||||||
|
|
||||||
private static boolean isRequest(int message) {
|
private static boolean isRequest(int message) {
|
||||||
@@ -61,29 +54,24 @@ public class CompanionTransportManager {
|
|||||||
return (message & 0xFF000000) == 0x33000000;
|
return (message & 0xFF000000) == 0x33000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface Listener {
|
||||||
|
void onRequestPermissionRestore(byte[] data);
|
||||||
|
}
|
||||||
|
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
|
|
||||||
@GuardedBy("mTransports")
|
@GuardedBy("mTransports")
|
||||||
private final SparseArray<Transport> mTransports = new SparseArray<>();
|
private final SparseArray<Transport> mTransports = new SparseArray<>();
|
||||||
|
|
||||||
@NonNull
|
@Nullable
|
||||||
private final Map<Integer, Listener> mListeners = new HashMap<>();
|
private Listener mListener;
|
||||||
|
|
||||||
private Transport mTempTransport;
|
|
||||||
|
|
||||||
public CompanionTransportManager(Context context) {
|
public CompanionTransportManager(Context context) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void setListener(@NonNull Listener listener) {
|
||||||
* Add a message listener when a message is received for the message type
|
mListener = listener;
|
||||||
*/
|
|
||||||
@GuardedBy("mTransports")
|
|
||||||
public void addListener(int message, @NonNull Listener listener) {
|
|
||||||
mListeners.put(message, listener);
|
|
||||||
for (int i = 0; i < mTransports.size(); i++) {
|
|
||||||
mTransports.valueAt(i).addListener(message, listener);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -117,7 +105,15 @@ public class CompanionTransportManager {
|
|||||||
detachSystemDataTransport(packageName, userId, associationId);
|
detachSystemDataTransport(packageName, userId, associationId);
|
||||||
}
|
}
|
||||||
|
|
||||||
initializeTransport(associationId, fd);
|
final Transport transport;
|
||||||
|
if (isSecureTransportEnabled(associationId)) {
|
||||||
|
transport = new SecureTransport(associationId, fd, mContext, mListener);
|
||||||
|
} else {
|
||||||
|
transport = new RawTransport(associationId, fd, mContext, mListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
transport.start();
|
||||||
|
mTransports.put(associationId, transport);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,85 +128,13 @@ public class CompanionTransportManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@GuardedBy("mTransports")
|
|
||||||
private void initializeTransport(int associationId, ParcelFileDescriptor fd) {
|
|
||||||
if (!isSecureTransportEnabled()) {
|
|
||||||
Transport transport = new RawTransport(associationId, fd, mContext);
|
|
||||||
for (Map.Entry<Integer, Listener> entry : mListeners.entrySet()) {
|
|
||||||
transport.addListener(entry.getKey(), entry.getValue());
|
|
||||||
}
|
|
||||||
transport.start();
|
|
||||||
mTransports.put(associationId, transport);
|
|
||||||
Slog.i(TAG, "RawTransport is created");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exchange platform info to decide which transport should be created
|
|
||||||
mTempTransport = new RawTransport(associationId, fd, mContext);
|
|
||||||
for (Map.Entry<Integer, Listener> entry : mListeners.entrySet()) {
|
|
||||||
mTempTransport.addListener(entry.getKey(), entry.getValue());
|
|
||||||
}
|
|
||||||
mTempTransport.addListener(MESSAGE_REQUEST_PLATFORM_INFO, this::onPlatformInfoReceived);
|
|
||||||
mTempTransport.start();
|
|
||||||
|
|
||||||
int sdk = Build.VERSION.SDK_INT;
|
|
||||||
String release = Build.VERSION.RELEASE;
|
|
||||||
// data format: | SDK_INT (int) | release length (int) | release |
|
|
||||||
final ByteBuffer data = ByteBuffer.allocate(4 + 4 + release.getBytes().length)
|
|
||||||
.putInt(sdk)
|
|
||||||
.putInt(release.getBytes().length)
|
|
||||||
.put(release.getBytes());
|
|
||||||
|
|
||||||
// TODO: it should check if preSharedKey is given
|
|
||||||
mTempTransport.requestForResponse(MESSAGE_REQUEST_PLATFORM_INFO, data.array());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Depending on the remote platform info to decide which transport should be created
|
|
||||||
*/
|
|
||||||
@GuardedBy("mTransports")
|
|
||||||
private void onPlatformInfoReceived(byte[] data) {
|
|
||||||
// TODO: it should check if preSharedKey is given
|
|
||||||
|
|
||||||
ByteBuffer buffer = ByteBuffer.wrap(data);
|
|
||||||
int remoteSdk = buffer.getInt();
|
|
||||||
byte[] remoteRelease = new byte[buffer.getInt()];
|
|
||||||
buffer.get(remoteRelease);
|
|
||||||
|
|
||||||
Slog.i(TAG, "Remote device SDK: " + remoteSdk + ", release:" + new String(remoteRelease));
|
|
||||||
|
|
||||||
Transport transport = mTempTransport;
|
|
||||||
mTempTransport = null;
|
|
||||||
|
|
||||||
int sdk = Build.VERSION.SDK_INT;
|
|
||||||
String release = Build.VERSION.RELEASE;
|
|
||||||
if (remoteSdk == NON_ANDROID) {
|
|
||||||
// TODO: pass in a real preSharedKey
|
|
||||||
transport = new SecureTransport(transport.getAssociationId(), transport.getFd(),
|
|
||||||
mContext, null, null);
|
|
||||||
} else if (sdk < Build.VERSION_CODES.UPSIDE_DOWN_CAKE
|
|
||||||
|| remoteSdk < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
|
||||||
// TODO: depending on the release version, either
|
|
||||||
// 1) using a RawTransport for old T versions
|
|
||||||
// 2) or an Ukey2 handshaked transport for UKey2 backported T versions
|
|
||||||
} else {
|
|
||||||
Slog.i(TAG, "Creating a secure channel");
|
|
||||||
transport = new SecureTransport(transport.getAssociationId(), transport.getFd(),
|
|
||||||
mContext);
|
|
||||||
for (Map.Entry<Integer, Listener> entry : mListeners.entrySet()) {
|
|
||||||
transport.addListener(entry.getKey(), entry.getValue());
|
|
||||||
}
|
|
||||||
transport.start();
|
|
||||||
}
|
|
||||||
mTransports.put(transport.getAssociationId(), transport);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Future<?> requestPermissionRestore(int associationId, byte[] data) {
|
public Future<?> requestPermissionRestore(int associationId, byte[] data) {
|
||||||
synchronized (mTransports) {
|
synchronized (mTransports) {
|
||||||
final Transport transport = mTransports.get(associationId);
|
final Transport transport = mTransports.get(associationId);
|
||||||
if (transport == null) {
|
if (transport == null) {
|
||||||
return CompletableFuture.failedFuture(new IOException("Missing transport"));
|
return CompletableFuture.failedFuture(new IOException("Missing transport"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return transport.requestForResponse(MESSAGE_REQUEST_PERMISSION_RESTORE, data);
|
return transport.requestForResponse(MESSAGE_REQUEST_PERMISSION_RESTORE, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -222,9 +146,10 @@ public class CompanionTransportManager {
|
|||||||
this.mSecureTransportEnabled = enabled;
|
this.mSecureTransportEnabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isSecureTransportEnabled() {
|
private boolean isSecureTransportEnabled(int associationId) {
|
||||||
boolean enabled = !Build.IS_DEBUGGABLE || mSecureTransportEnabled;
|
boolean enabled = !Build.IS_DEBUGGABLE || mSecureTransportEnabled;
|
||||||
|
|
||||||
|
// TODO: version comparison logic
|
||||||
return enabled;
|
return enabled;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,51 +16,51 @@
|
|||||||
|
|
||||||
package com.android.server.companion.transport;
|
package com.android.server.companion.transport;
|
||||||
|
|
||||||
|
import android.security.keystore.KeyGenParameterSpec;
|
||||||
|
import android.security.keystore.KeyProperties;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
import java.security.InvalidKeyException;
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.KeyStore;
|
||||||
|
import java.security.KeyStoreException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.Arrays;
|
import java.security.UnrecoverableEntryException;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
|
||||||
import javax.crypto.BadPaddingException;
|
import javax.crypto.BadPaddingException;
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import javax.crypto.IllegalBlockSizeException;
|
import javax.crypto.IllegalBlockSizeException;
|
||||||
|
import javax.crypto.KeyGenerator;
|
||||||
import javax.crypto.NoSuchPaddingException;
|
import javax.crypto.NoSuchPaddingException;
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import javax.crypto.spec.IvParameterSpec;
|
import javax.crypto.spec.IvParameterSpec;
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class uses Java Cryptography to encrypt and decrypt messages
|
* This class can be used to encrypt and decrypt bytes using Android Cryptography
|
||||||
*/
|
*/
|
||||||
public class CryptoManager {
|
public class CryptoManager {
|
||||||
|
|
||||||
private static final String TAG = "CDM_CryptoManager";
|
private static final String TAG = "CDM_CryptoManager";
|
||||||
private static final int SECRET_KEY_LENGTH = 32;
|
|
||||||
private static final String ALGORITHM = "AES";
|
|
||||||
private static final String TRANSFORMATION = "AES/CBC/PKCS7Padding";
|
|
||||||
|
|
||||||
private final byte[] mPreSharedKey;
|
private static final String KEY_STORE_ALIAS = "cdm_secret";
|
||||||
private Cipher mEncryptCipher;
|
private static final String ALGORITHM = KeyProperties.KEY_ALGORITHM_AES;
|
||||||
private Cipher mDecryptCipher;
|
private static final String BLOCK_MODE = KeyProperties.BLOCK_MODE_CBC;
|
||||||
|
private static final String PADDING = KeyProperties.ENCRYPTION_PADDING_PKCS7;
|
||||||
|
private static final String TRANSFORMATION = ALGORITHM + "/" + BLOCK_MODE + "/" + PADDING;
|
||||||
|
|
||||||
private SecretKey mSecretKey;
|
private final KeyStore mKeyStore;
|
||||||
|
|
||||||
public CryptoManager(byte[] preSharedKey) {
|
public CryptoManager() {
|
||||||
if (preSharedKey == null) {
|
// Initialize KeyStore
|
||||||
mPreSharedKey = Arrays.copyOf(new byte[0], SECRET_KEY_LENGTH);
|
|
||||||
} else {
|
|
||||||
mPreSharedKey = Arrays.copyOf(preSharedKey, SECRET_KEY_LENGTH);
|
|
||||||
}
|
|
||||||
mSecretKey = new SecretKeySpec(mPreSharedKey, ALGORITHM);
|
|
||||||
try {
|
try {
|
||||||
mEncryptCipher = Cipher.getInstance(TRANSFORMATION);
|
mKeyStore = KeyStore.getInstance("AndroidKeyStore");
|
||||||
mEncryptCipher.init(Cipher.ENCRYPT_MODE, mSecretKey);
|
mKeyStore.load(null);
|
||||||
mDecryptCipher = Cipher.getInstance(TRANSFORMATION);
|
} catch (KeyStoreException | IOException | NoSuchAlgorithmException
|
||||||
} catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException e) {
|
| CertificateException e) {
|
||||||
Slog.e(TAG, e.getMessage());
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,19 +69,21 @@ public class CryptoManager {
|
|||||||
*/
|
*/
|
||||||
public byte[] encrypt(byte[] input) {
|
public byte[] encrypt(byte[] input) {
|
||||||
try {
|
try {
|
||||||
if (mEncryptCipher == null) {
|
// Encrypt using Cipher
|
||||||
return null;
|
Cipher encryptCipher = Cipher.getInstance(TRANSFORMATION);
|
||||||
}
|
encryptCipher.init(Cipher.ENCRYPT_MODE, getKey());
|
||||||
|
byte[] encryptedBytes = encryptCipher.doFinal(input);
|
||||||
|
|
||||||
byte[] encryptedBytes = mEncryptCipher.doFinal(input);
|
// Write to bytes
|
||||||
ByteBuffer buffer = ByteBuffer.allocate(
|
ByteBuffer buffer = ByteBuffer.allocate(
|
||||||
4 + mEncryptCipher.getIV().length + 4 + encryptedBytes.length)
|
4 + encryptCipher.getIV().length + 4 + encryptedBytes.length)
|
||||||
.putInt(mEncryptCipher.getIV().length)
|
.putInt(encryptCipher.getIV().length)
|
||||||
.put(mEncryptCipher.getIV())
|
.put(encryptCipher.getIV())
|
||||||
.putInt(encryptedBytes.length)
|
.putInt(encryptedBytes.length)
|
||||||
.put(encryptedBytes);
|
.put(encryptedBytes);
|
||||||
return buffer.array();
|
return buffer.array();
|
||||||
} catch (IllegalBlockSizeException | BadPaddingException e) {
|
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
|
||||||
|
| IllegalBlockSizeException | BadPaddingException e) {
|
||||||
Slog.e(TAG, e.getMessage());
|
Slog.e(TAG, e.getMessage());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -97,20 +99,45 @@ public class CryptoManager {
|
|||||||
byte[] encryptedBytes = new byte[buffer.getInt()];
|
byte[] encryptedBytes = new byte[buffer.getInt()];
|
||||||
buffer.get(encryptedBytes);
|
buffer.get(encryptedBytes);
|
||||||
try {
|
try {
|
||||||
mDecryptCipher.init(Cipher.DECRYPT_MODE, getKey(), new IvParameterSpec(iv));
|
Cipher decryptCipher = Cipher.getInstance(TRANSFORMATION);
|
||||||
return mDecryptCipher.doFinal(encryptedBytes);
|
decryptCipher.init(Cipher.DECRYPT_MODE, getKey(), new IvParameterSpec(iv));
|
||||||
} catch (InvalidKeyException | InvalidAlgorithmParameterException
|
return decryptCipher.doFinal(encryptedBytes);
|
||||||
| IllegalBlockSizeException | BadPaddingException e) {
|
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
|
||||||
|
| InvalidAlgorithmParameterException | IllegalBlockSizeException
|
||||||
|
| BadPaddingException e) {
|
||||||
Slog.e(TAG, e.getMessage());
|
Slog.e(TAG, e.getMessage());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private SecretKey getKey() {
|
private SecretKey getKey() {
|
||||||
if (mSecretKey != null) {
|
try {
|
||||||
return mSecretKey;
|
KeyStore.Entry keyEntry = mKeyStore.getEntry(KEY_STORE_ALIAS, null);
|
||||||
|
if (keyEntry instanceof KeyStore.SecretKeyEntry
|
||||||
|
&& ((KeyStore.SecretKeyEntry) keyEntry).getSecretKey() != null) {
|
||||||
|
return ((KeyStore.SecretKeyEntry) keyEntry).getSecretKey();
|
||||||
|
} else {
|
||||||
|
return createKey();
|
||||||
|
}
|
||||||
|
} catch (NoSuchAlgorithmException | UnrecoverableEntryException | KeyStoreException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private SecretKey createKey() {
|
||||||
|
try {
|
||||||
|
KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM);
|
||||||
|
keyGenerator.init(
|
||||||
|
new KeyGenParameterSpec.Builder(KEY_STORE_ALIAS,
|
||||||
|
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
|
||||||
|
.setBlockModes(BLOCK_MODE)
|
||||||
|
.setEncryptionPaddings(PADDING)
|
||||||
|
.setUserAuthenticationRequired(false)
|
||||||
|
.setRandomizedEncryptionRequired(true)
|
||||||
|
.build());
|
||||||
|
return keyGenerator.generateKey();
|
||||||
|
} catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
mSecretKey = new SecretKeySpec(mPreSharedKey, ALGORITHM);
|
|
||||||
return mSecretKey;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ import android.content.Context;
|
|||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
|
|
||||||
|
import com.android.server.companion.transport.CompanionTransportManager.Listener;
|
||||||
|
|
||||||
import libcore.io.IoUtils;
|
import libcore.io.IoUtils;
|
||||||
import libcore.io.Streams;
|
import libcore.io.Streams;
|
||||||
|
|
||||||
@@ -30,8 +32,8 @@ import java.nio.ByteBuffer;
|
|||||||
class RawTransport extends Transport {
|
class RawTransport extends Transport {
|
||||||
private volatile boolean mStopped;
|
private volatile boolean mStopped;
|
||||||
|
|
||||||
RawTransport(int associationId, ParcelFileDescriptor fd, Context context) {
|
RawTransport(int associationId, ParcelFileDescriptor fd, Context context, Listener listener) {
|
||||||
super(associationId, fd, context);
|
super(associationId, fd, context, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -62,7 +64,7 @@ class RawTransport extends Transport {
|
|||||||
protected void sendMessage(int message, int sequence, @NonNull byte[] data)
|
protected void sendMessage(int message, int sequence, @NonNull byte[] data)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.e(TAG, "Sending message 0x" + Integer.toHexString(message)
|
Slog.d(TAG, "Sending message 0x" + Integer.toHexString(message)
|
||||||
+ " sequence " + sequence + " length " + data.length
|
+ " sequence " + sequence + " length " + data.length
|
||||||
+ " to association " + mAssociationId);
|
+ " to association " + mAssociationId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ import android.content.Context;
|
|||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
|
|
||||||
import com.android.server.companion.securechannel.AttestationVerifier;
|
|
||||||
import com.android.server.companion.securechannel.SecureChannel;
|
import com.android.server.companion.securechannel.SecureChannel;
|
||||||
|
import com.android.server.companion.transport.CompanionTransportManager.Listener;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
@@ -37,17 +37,14 @@ class SecureTransport extends Transport implements SecureChannel.Callback {
|
|||||||
|
|
||||||
private final BlockingQueue<byte[]> mRequestQueue = new ArrayBlockingQueue<>(100);
|
private final BlockingQueue<byte[]> mRequestQueue = new ArrayBlockingQueue<>(100);
|
||||||
|
|
||||||
SecureTransport(int associationId, ParcelFileDescriptor fd, Context context) {
|
SecureTransport(int associationId,
|
||||||
super(associationId, fd, context);
|
ParcelFileDescriptor fd,
|
||||||
|
Context context,
|
||||||
|
Listener listener) {
|
||||||
|
super(associationId, fd, context, listener);
|
||||||
mSecureChannel = new SecureChannel(mRemoteIn, mRemoteOut, this, context);
|
mSecureChannel = new SecureChannel(mRemoteIn, mRemoteOut, this, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
SecureTransport(int associationId, ParcelFileDescriptor fd, Context context,
|
|
||||||
byte[] preSharedKey, AttestationVerifier verifier) {
|
|
||||||
super(associationId, fd, context);
|
|
||||||
mSecureChannel = new SecureChannel(mRemoteIn, mRemoteOut, this, preSharedKey, verifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start() {
|
public void start() {
|
||||||
mSecureChannel.start();
|
mSecureChannel.start();
|
||||||
|
|||||||
@@ -25,28 +25,23 @@ import android.util.Slog;
|
|||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
|
|
||||||
import com.android.internal.annotations.GuardedBy;
|
import com.android.internal.annotations.GuardedBy;
|
||||||
|
import com.android.server.companion.transport.CompanionTransportManager.Listener;
|
||||||
|
|
||||||
import libcore.util.EmptyArray;
|
import libcore.util.EmptyArray;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
/**
|
abstract class Transport {
|
||||||
* This class represents the channel established between two devices.
|
|
||||||
*/
|
|
||||||
public abstract class Transport {
|
|
||||||
protected static final String TAG = "CDM_CompanionTransport";
|
protected static final String TAG = "CDM_CompanionTransport";
|
||||||
protected static final boolean DEBUG = Build.IS_DEBUGGABLE;
|
protected static final boolean DEBUG = Build.IS_DEBUGGABLE;
|
||||||
|
|
||||||
static final int MESSAGE_REQUEST_PING = 0x63807378; // ?PIN
|
static final int MESSAGE_REQUEST_PING = 0x63807378; // ?PIN
|
||||||
public static final int MESSAGE_REQUEST_PLATFORM_INFO = 0x63807086; // ?PFV
|
static final int MESSAGE_REQUEST_PERMISSION_RESTORE = 0x63826983; // ?RES
|
||||||
public static final int MESSAGE_REQUEST_PERMISSION_RESTORE = 0x63826983; // ?RES
|
|
||||||
|
|
||||||
static final int MESSAGE_RESPONSE_SUCCESS = 0x33838567; // !SUC
|
static final int MESSAGE_RESPONSE_SUCCESS = 0x33838567; // !SUC
|
||||||
static final int MESSAGE_RESPONSE_FAILURE = 0x33706573; // !FAI
|
static final int MESSAGE_RESPONSE_FAILURE = 0x33706573; // !FAI
|
||||||
@@ -54,24 +49,11 @@ public abstract class Transport {
|
|||||||
protected static final int HEADER_LENGTH = 12;
|
protected static final int HEADER_LENGTH = 12;
|
||||||
|
|
||||||
protected final int mAssociationId;
|
protected final int mAssociationId;
|
||||||
protected final ParcelFileDescriptor mFd;
|
|
||||||
protected final InputStream mRemoteIn;
|
protected final InputStream mRemoteIn;
|
||||||
protected final OutputStream mRemoteOut;
|
protected final OutputStream mRemoteOut;
|
||||||
protected final Context mContext;
|
protected final Context mContext;
|
||||||
|
|
||||||
/** Message type -> Listener */
|
private final Listener mListener;
|
||||||
private final Map<Integer, Listener> mListeners;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Message listener
|
|
||||||
*/
|
|
||||||
public interface Listener {
|
|
||||||
/**
|
|
||||||
* Called when a message is received
|
|
||||||
* @param data data content in the message
|
|
||||||
*/
|
|
||||||
void onDataReceived(byte[] data);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isRequest(int message) {
|
private static boolean isRequest(int message) {
|
||||||
return (message & 0xFF000000) == 0x63000000;
|
return (message & 0xFF000000) == 0x63000000;
|
||||||
@@ -86,36 +68,16 @@ public abstract class Transport {
|
|||||||
new SparseArray<>();
|
new SparseArray<>();
|
||||||
protected final AtomicInteger mNextSequence = new AtomicInteger();
|
protected final AtomicInteger mNextSequence = new AtomicInteger();
|
||||||
|
|
||||||
Transport(int associationId, ParcelFileDescriptor fd, Context context) {
|
Transport(int associationId, ParcelFileDescriptor fd, Context context, Listener listener) {
|
||||||
mAssociationId = associationId;
|
this.mAssociationId = associationId;
|
||||||
mFd = fd;
|
this.mRemoteIn = new ParcelFileDescriptor.AutoCloseInputStream(fd);
|
||||||
mRemoteIn = new ParcelFileDescriptor.AutoCloseInputStream(fd);
|
this.mRemoteOut = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
|
||||||
mRemoteOut = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
|
this.mContext = context;
|
||||||
mContext = context;
|
this.mListener = listener;
|
||||||
mListeners = new HashMap<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a listener when a message is received for the message type
|
|
||||||
* @param message Message type
|
|
||||||
* @param listener Execute when a message with the type is received
|
|
||||||
*/
|
|
||||||
public void addListener(int message, Listener listener) {
|
|
||||||
mListeners.put(message, listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getAssociationId() {
|
|
||||||
return mAssociationId;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ParcelFileDescriptor getFd() {
|
|
||||||
return mFd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void start();
|
public abstract void start();
|
||||||
public abstract void stop();
|
public abstract void stop();
|
||||||
protected abstract void sendMessage(int message, int sequence, @NonNull byte[] data)
|
|
||||||
throws IOException;
|
|
||||||
|
|
||||||
public Future<byte[]> requestForResponse(int message, byte[] data) {
|
public Future<byte[]> requestForResponse(int message, byte[] data) {
|
||||||
if (DEBUG) Slog.d(TAG, "Requesting for response");
|
if (DEBUG) Slog.d(TAG, "Requesting for response");
|
||||||
@@ -137,6 +99,9 @@ public abstract class Transport {
|
|||||||
return pending;
|
return pending;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected abstract void sendMessage(int message, int sequence, @NonNull byte[] data)
|
||||||
|
throws IOException;
|
||||||
|
|
||||||
protected final void handleMessage(int message, int sequence, @NonNull byte[] data)
|
protected final void handleMessage(int message, int sequence, @NonNull byte[] data)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
@@ -165,11 +130,6 @@ public abstract class Transport {
|
|||||||
sendMessage(MESSAGE_RESPONSE_SUCCESS, sequence, data);
|
sendMessage(MESSAGE_RESPONSE_SUCCESS, sequence, data);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MESSAGE_REQUEST_PLATFORM_INFO: {
|
|
||||||
callback(message, data);
|
|
||||||
sendMessage(MESSAGE_RESPONSE_SUCCESS, sequence, EmptyArray.BYTE);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case MESSAGE_REQUEST_PERMISSION_RESTORE: {
|
case MESSAGE_REQUEST_PERMISSION_RESTORE: {
|
||||||
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)
|
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)
|
||||||
&& !Build.isDebuggable()) {
|
&& !Build.isDebuggable()) {
|
||||||
@@ -178,7 +138,7 @@ public abstract class Transport {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
callback(message, data);
|
mListener.onRequestPermissionRestore(data);
|
||||||
sendMessage(MESSAGE_RESPONSE_SUCCESS, sequence, EmptyArray.BYTE);
|
sendMessage(MESSAGE_RESPONSE_SUCCESS, sequence, EmptyArray.BYTE);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Slog.w(TAG, "Failed to restore permissions");
|
Slog.w(TAG, "Failed to restore permissions");
|
||||||
@@ -194,12 +154,6 @@ public abstract class Transport {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void callback(int message, byte[] data) {
|
|
||||||
if (mListeners.containsKey(message)) {
|
|
||||||
mListeners.get(message).onDataReceived(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processResponse(int message, int sequence, byte[] data) {
|
private void processResponse(int message, int sequence, byte[] data) {
|
||||||
final CompletableFuture<byte[]> future;
|
final CompletableFuture<byte[]> future;
|
||||||
synchronized (mPendingRequests) {
|
synchronized (mPendingRequests) {
|
||||||
|
|||||||
Reference in New Issue
Block a user