Fix ADB key file reading

The "adb_temp_keys.xml" file is used to keep track of each ADB key and
when it was previously connected. It also keeps track of authorized WiFi
networks. It is written correctly, however, the parser currently is
unable to read the file that it wrote, meaning that:
1. ADB keys never properly expire
2. WiFi APs are never properly restored

Furthermore, because of complex interactions with restoring the adb_keys
file in Test Harness Mode, duplicate keys get appended repteatedly to
the adb_keys file if they're not in the key map.

This cleans up the file, making it more testable, adds tests for the
previously broken functionality, and just performs some simple
restructuring of bits of the code that were error-prone.

Now, the adb_keys file is nothing but a view of the temp keys file. When
keys are added to the temp keys file, we completely rewrite the adb_keys
file with the set of keys present in the temp keys file, meaning that
duplicates cannot be added.

This also introduces a method for other system services (primarily for
TestHarnessModeService) to notify the AdbDebuggingManager that the key
files were modified, and it should reload the state.

Bug: 236299256
Test: atest AdbDebuggingManagerTest

Change-Id: Iae955aca17e873288d58b16f184bd4d325d50d86
This commit is contained in:
William Hester
2022-06-15 17:24:50 -07:00
parent dfca3dbdb6
commit efb8194ec4
5 changed files with 405 additions and 358 deletions

View File

@@ -54,6 +54,12 @@ public abstract class AdbManagerInternal {
*/
public abstract File getAdbTempKeysFile();
/**
* Notify the AdbManager that the key files have changed and any in-memory state should be
* reloaded.
*/
public abstract void notifyKeyFilesUpdated();
/**
* Starts adbd for a transport.
*/

View File

@@ -19,7 +19,7 @@ package com.android.server.adb;
import static com.android.internal.util.dump.DumpUtils.writeStringIfNotNull;
import android.annotation.NonNull;
import android.annotation.TestApi;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -102,11 +102,26 @@ import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Provides communication to the Android Debug Bridge daemon to allow, deny, or clear public keysi
* Provides communication to the Android Debug Bridge daemon to allow, deny, or clear public keys
* that are authorized to connect to the ADB service itself.
*
* <p>The AdbDebuggingManager controls two files:
* <ol>
* <li>adb_keys
* <li>adb_temp_keys.xml
* </ol>
*
* <p>The ADB Daemon (adbd) reads <em>only</em> the adb_keys file for authorization. Public keys
* from registered hosts are stored in adb_keys, one entry per line.
*
* <p>AdbDebuggingManager also keeps adb_temp_keys.xml, which is used for two things
* <ol>
* <li>Removing unused keys from the adb_keys file
* <li>Managing authorized WiFi access points for ADB over WiFi
* </ol>
*/
public class AdbDebuggingManager {
private static final String TAG = "AdbDebuggingManager";
private static final String TAG = AdbDebuggingManager.class.getSimpleName();
private static final boolean DEBUG = false;
private static final boolean MDNS_DEBUG = false;
@@ -118,18 +133,20 @@ public class AdbDebuggingManager {
// as a subsequent connection occurs within the allowed duration.
private static final String ADB_TEMP_KEYS_FILE = "adb_temp_keys.xml";
private static final int BUFFER_SIZE = 65536;
private static final Ticker SYSTEM_TICKER = () -> System.currentTimeMillis();
private final Context mContext;
private final ContentResolver mContentResolver;
private final Handler mHandler;
private AdbDebuggingThread mThread;
@VisibleForTesting final AdbDebuggingHandler mHandler;
@Nullable private AdbDebuggingThread mThread;
private boolean mAdbUsbEnabled = false;
private boolean mAdbWifiEnabled = false;
private String mFingerprints;
// A key can be used more than once (e.g. USB, wifi), so need to keep a refcount
private final Map<String, Integer> mConnectedKeys;
private String mConfirmComponent;
private final File mTestUserKeyFile;
private final Map<String, Integer> mConnectedKeys = new HashMap<>();
private final String mConfirmComponent;
@Nullable private final File mUserKeyFile;
@Nullable private final File mTempKeysFile;
private static final String WIFI_PERSISTENT_CONFIG_PROPERTY =
"persist.adb.tls_server.enable";
@@ -138,37 +155,44 @@ public class AdbDebuggingManager {
private static final int PAIRING_CODE_LENGTH = 6;
private PairingThread mPairingThread = null;
// A list of keys connected via wifi
private final Set<String> mWifiConnectedKeys;
private final Set<String> mWifiConnectedKeys = new HashSet<>();
// The current info of the adbwifi connection.
private AdbConnectionInfo mAdbConnectionInfo;
private AdbConnectionInfo mAdbConnectionInfo = new AdbConnectionInfo();
// Polls for a tls port property when adb wifi is enabled
private AdbConnectionPortPoller mConnectionPortPoller;
private final PortListenerImpl mPortListener = new PortListenerImpl();
private final Ticker mTicker;
public AdbDebuggingManager(Context context) {
mHandler = new AdbDebuggingHandler(FgThread.get().getLooper());
mContext = context;
mContentResolver = mContext.getContentResolver();
mTestUserKeyFile = null;
mConnectedKeys = new HashMap<String, Integer>();
mWifiConnectedKeys = new HashSet<String>();
mAdbConnectionInfo = new AdbConnectionInfo();
this(
context,
/* confirmComponent= */ null,
getAdbFile(ADB_KEYS_FILE),
getAdbFile(ADB_TEMP_KEYS_FILE),
/* adbDebuggingThread= */ null,
SYSTEM_TICKER);
}
/**
* Constructor that accepts the component to be invoked to confirm if the user wants to allow
* an adb connection from the key.
*/
@TestApi
protected AdbDebuggingManager(Context context, String confirmComponent, File testUserKeyFile) {
mHandler = new AdbDebuggingHandler(FgThread.get().getLooper());
@VisibleForTesting
AdbDebuggingManager(
Context context,
String confirmComponent,
File testUserKeyFile,
File tempKeysFile,
AdbDebuggingThread adbDebuggingThread,
Ticker ticker) {
mContext = context;
mContentResolver = mContext.getContentResolver();
mConfirmComponent = confirmComponent;
mTestUserKeyFile = testUserKeyFile;
mConnectedKeys = new HashMap<String, Integer>();
mWifiConnectedKeys = new HashSet<String>();
mAdbConnectionInfo = new AdbConnectionInfo();
mUserKeyFile = testUserKeyFile;
mTempKeysFile = tempKeysFile;
mThread = adbDebuggingThread;
mTicker = ticker;
mHandler = new AdbDebuggingHandler(FgThread.get().getLooper(), mThread);
}
static void sendBroadcastWithDebugPermission(@NonNull Context context, @NonNull Intent intent,
@@ -189,8 +213,7 @@ public class AdbDebuggingManager {
// consisting of only letters, digits, and hyphens, must begin and end
// with a letter or digit, must not contain consecutive hyphens, and
// must contain at least one letter.
@VisibleForTesting
static final String SERVICE_PROTOCOL = "adb-tls-pairing";
@VisibleForTesting static final String SERVICE_PROTOCOL = "adb-tls-pairing";
private final String mServiceType = String.format("_%s._tcp.", SERVICE_PROTOCOL);
private int mPort;
@@ -352,16 +375,24 @@ public class AdbDebuggingManager {
}
}
class AdbDebuggingThread extends Thread {
@VisibleForTesting
static class AdbDebuggingThread extends Thread {
private boolean mStopped;
private LocalSocket mSocket;
private OutputStream mOutputStream;
private InputStream mInputStream;
private Handler mHandler;
@VisibleForTesting
AdbDebuggingThread() {
super(TAG);
}
@VisibleForTesting
void setHandler(Handler handler) {
mHandler = handler;
}
@Override
public void run() {
if (DEBUG) Slog.d(TAG, "Entering thread");
@@ -536,7 +567,7 @@ public class AdbDebuggingManager {
}
}
class AdbConnectionInfo {
private static class AdbConnectionInfo {
private String mBssid;
private String mSsid;
private int mPort;
@@ -743,11 +774,14 @@ public class AdbDebuggingManager {
// Notification when adbd socket is disconnected.
static final int MSG_ADBD_SOCKET_DISCONNECTED = 27;
// === Messages from other parts of the system
private static final int MESSAGE_KEY_FILES_UPDATED = 28;
// === Messages we can send to adbd ===========
static final String MSG_DISCONNECT_DEVICE = "DD";
static final String MSG_DISABLE_ADBDWIFI = "DA";
private AdbKeyStore mAdbKeyStore;
@Nullable @VisibleForTesting AdbKeyStore mAdbKeyStore;
// Usb, Wi-Fi transports can be enabled together or separately, so don't break the framework
// connection unless all transport types are disconnected.
@@ -762,19 +796,19 @@ public class AdbDebuggingManager {
}
};
AdbDebuggingHandler(Looper looper) {
super(looper);
}
/**
* Constructor that accepts the AdbDebuggingThread to which responses should be sent
* and the AdbKeyStore to be used to store the temporary grants.
*/
@TestApi
AdbDebuggingHandler(Looper looper, AdbDebuggingThread thread, AdbKeyStore adbKeyStore) {
/** Constructor that accepts the AdbDebuggingThread to which responses should be sent. */
@VisibleForTesting
AdbDebuggingHandler(Looper looper, AdbDebuggingThread thread) {
super(looper);
mThread = thread;
mAdbKeyStore = adbKeyStore;
}
/** Initialize the AdbKeyStore so tests can grab mAdbKeyStore immediately. */
@VisibleForTesting
void initKeyStore() {
if (mAdbKeyStore == null) {
mAdbKeyStore = new AdbKeyStore();
}
}
// Show when at least one device is connected.
@@ -805,6 +839,7 @@ public class AdbDebuggingManager {
registerForAuthTimeChanges();
mThread = new AdbDebuggingThread();
mThread.setHandler(mHandler);
mThread.start();
mAdbKeyStore.updateKeyStore();
@@ -825,8 +860,7 @@ public class AdbDebuggingManager {
if (!mConnectedKeys.isEmpty()) {
for (Map.Entry<String, Integer> entry : mConnectedKeys.entrySet()) {
mAdbKeyStore.setLastConnectionTime(entry.getKey(),
System.currentTimeMillis());
mAdbKeyStore.setLastConnectionTime(entry.getKey(), mTicker.currentTimeMillis());
}
sendPersistKeyStoreMessage();
mConnectedKeys.clear();
@@ -836,9 +870,7 @@ public class AdbDebuggingManager {
}
public void handleMessage(Message msg) {
if (mAdbKeyStore == null) {
mAdbKeyStore = new AdbKeyStore();
}
initKeyStore();
switch (msg.what) {
case MESSAGE_ADB_ENABLED:
@@ -873,7 +905,7 @@ public class AdbDebuggingManager {
if (!mConnectedKeys.containsKey(key)) {
mConnectedKeys.put(key, 1);
}
mAdbKeyStore.setLastConnectionTime(key, System.currentTimeMillis());
mAdbKeyStore.setLastConnectionTime(key, mTicker.currentTimeMillis());
sendPersistKeyStoreMessage();
scheduleJobToUpdateAdbKeyStore();
}
@@ -920,9 +952,7 @@ public class AdbDebuggingManager {
mConnectedKeys.clear();
// If the key store has not yet been instantiated then do so now; this avoids
// the unnecessary creation of the key store when adb is not enabled.
if (mAdbKeyStore == null) {
mAdbKeyStore = new AdbKeyStore();
}
initKeyStore();
mWifiConnectedKeys.clear();
mAdbKeyStore.deleteKeyStore();
cancelJobToUpdateAdbKeyStore();
@@ -937,7 +967,8 @@ public class AdbDebuggingManager {
alwaysAllow = true;
int refcount = mConnectedKeys.get(key) - 1;
if (refcount == 0) {
mAdbKeyStore.setLastConnectionTime(key, System.currentTimeMillis());
mAdbKeyStore.setLastConnectionTime(
key, mTicker.currentTimeMillis());
sendPersistKeyStoreMessage();
scheduleJobToUpdateAdbKeyStore();
mConnectedKeys.remove(key);
@@ -963,7 +994,7 @@ public class AdbDebuggingManager {
if (!mConnectedKeys.isEmpty()) {
for (Map.Entry<String, Integer> entry : mConnectedKeys.entrySet()) {
mAdbKeyStore.setLastConnectionTime(entry.getKey(),
System.currentTimeMillis());
mTicker.currentTimeMillis());
}
sendPersistKeyStoreMessage();
scheduleJobToUpdateAdbKeyStore();
@@ -984,7 +1015,7 @@ public class AdbDebuggingManager {
} else {
mConnectedKeys.put(key, mConnectedKeys.get(key) + 1);
}
mAdbKeyStore.setLastConnectionTime(key, System.currentTimeMillis());
mAdbKeyStore.setLastConnectionTime(key, mTicker.currentTimeMillis());
sendPersistKeyStoreMessage();
scheduleJobToUpdateAdbKeyStore();
logAdbConnectionChanged(key, AdbProtoEnums.AUTOMATICALLY_ALLOWED, true);
@@ -1206,6 +1237,10 @@ public class AdbDebuggingManager {
}
break;
}
case MESSAGE_KEY_FILES_UPDATED: {
mAdbKeyStore.reloadKeyMap();
break;
}
}
}
@@ -1377,8 +1412,7 @@ public class AdbDebuggingManager {
AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent,
UserHandle.ALL);
// Add the key into the keystore
mAdbKeyStore.setLastConnectionTime(publicKey,
System.currentTimeMillis());
mAdbKeyStore.setLastConnectionTime(publicKey, mTicker.currentTimeMillis());
sendPersistKeyStoreMessage();
scheduleJobToUpdateAdbKeyStore();
}
@@ -1449,19 +1483,13 @@ public class AdbDebuggingManager {
extras.add(new AbstractMap.SimpleEntry<String, String>("ssid", ssid));
extras.add(new AbstractMap.SimpleEntry<String, String>("bssid", bssid));
int currentUserId = ActivityManager.getCurrentUser();
UserInfo userInfo = UserManager.get(mContext).getUserInfo(currentUserId);
String componentString;
if (userInfo.isAdmin()) {
componentString = Resources.getSystem().getString(
com.android.internal.R.string.config_customAdbWifiNetworkConfirmationComponent);
} else {
componentString = Resources.getSystem().getString(
com.android.internal.R.string.config_customAdbWifiNetworkConfirmationComponent);
}
String componentString =
Resources.getSystem().getString(
R.string.config_customAdbWifiNetworkConfirmationComponent);
ComponentName componentName = ComponentName.unflattenFromString(componentString);
UserInfo userInfo = UserManager.get(mContext).getUserInfo(currentUserId);
if (startConfirmationActivity(componentName, userInfo.getUserHandle(), extras)
|| startConfirmationService(componentName, userInfo.getUserHandle(),
extras)) {
|| startConfirmationService(componentName, userInfo.getUserHandle(), extras)) {
return;
}
Slog.e(TAG, "Unable to start customAdbWifiNetworkConfirmation[SecondaryUser]Component "
@@ -1543,7 +1571,7 @@ public class AdbDebuggingManager {
/**
* Returns a new File with the specified name in the adb directory.
*/
private File getAdbFile(String fileName) {
private static File getAdbFile(String fileName) {
File dataDir = Environment.getDataDirectory();
File adbDir = new File(dataDir, ADB_DIRECTORY);
@@ -1556,66 +1584,38 @@ public class AdbDebuggingManager {
}
File getAdbTempKeysFile() {
return getAdbFile(ADB_TEMP_KEYS_FILE);
return mTempKeysFile;
}
File getUserKeyFile() {
return mTestUserKeyFile == null ? getAdbFile(ADB_KEYS_FILE) : mTestUserKeyFile;
}
private void writeKey(String key) {
try {
File keyFile = getUserKeyFile();
if (keyFile == null) {
return;
}
FileOutputStream fo = new FileOutputStream(keyFile, true);
fo.write(key.getBytes());
fo.write('\n');
fo.close();
FileUtils.setPermissions(keyFile.toString(),
FileUtils.S_IRUSR | FileUtils.S_IWUSR | FileUtils.S_IRGRP, -1, -1);
} catch (IOException ex) {
Slog.e(TAG, "Error writing key:" + ex);
}
return mUserKeyFile;
}
private void writeKeys(Iterable<String> keys) {
AtomicFile atomicKeyFile = null;
if (mUserKeyFile == null) {
return;
}
AtomicFile atomicKeyFile = new AtomicFile(mUserKeyFile);
// Note: Do not use a try-with-resources with the FileOutputStream, because AtomicFile
// requires that it's cleaned up with AtomicFile.failWrite();
FileOutputStream fo = null;
try {
File keyFile = getUserKeyFile();
if (keyFile == null) {
return;
}
atomicKeyFile = new AtomicFile(keyFile);
fo = atomicKeyFile.startWrite();
for (String key : keys) {
fo.write(key.getBytes());
fo.write('\n');
}
atomicKeyFile.finishWrite(fo);
FileUtils.setPermissions(keyFile.toString(),
FileUtils.S_IRUSR | FileUtils.S_IWUSR | FileUtils.S_IRGRP, -1, -1);
} catch (IOException ex) {
Slog.e(TAG, "Error writing keys: " + ex);
if (atomicKeyFile != null) {
atomicKeyFile.failWrite(fo);
}
atomicKeyFile.failWrite(fo);
return;
}
}
private void deleteKeyFile() {
File keyFile = getUserKeyFile();
if (keyFile != null) {
keyFile.delete();
}
FileUtils.setPermissions(
mUserKeyFile.toString(),
FileUtils.S_IRUSR | FileUtils.S_IWUSR | FileUtils.S_IRGRP, -1, -1);
}
/**
@@ -1744,6 +1744,13 @@ public class AdbDebuggingManager {
return mAdbWifiEnabled;
}
/**
* Notify that they key files were updated so the AdbKeyManager reloads the keys.
*/
public void notifyKeyFilesUpdated() {
mHandler.sendEmptyMessage(AdbDebuggingHandler.MESSAGE_KEY_FILES_UPDATED);
}
/**
* Sends a message to the handler to persist the keystore.
*/
@@ -1778,7 +1785,7 @@ public class AdbDebuggingManager {
try {
dump.write("keystore", AdbDebuggingManagerProto.KEYSTORE,
FileUtils.readTextFile(getAdbTempKeysFile(), 0, null));
FileUtils.readTextFile(mTempKeysFile, 0, null));
} catch (IOException e) {
Slog.i(TAG, "Cannot read keystore: ", e);
}
@@ -1792,12 +1799,12 @@ public class AdbDebuggingManager {
* ADB_ALLOWED_CONNECTION_TIME setting.
*/
class AdbKeyStore {
private Map<String, Long> mKeyMap;
private Set<String> mSystemKeys;
private File mKeyFile;
private AtomicFile mAtomicKeyFile;
private List<String> mTrustedNetworks;
private final Set<String> mSystemKeys;
private final Map<String, Long> mKeyMap = new HashMap<>();
private final List<String> mTrustedNetworks = new ArrayList<>();
private static final int KEYSTORE_VERSION = 1;
private static final int MAX_SUPPORTED_KEYSTORE_VERSION = 1;
private static final String XML_KEYSTORE_START_TAG = "keyStore";
@@ -1819,26 +1826,22 @@ public class AdbDebuggingManager {
public static final long NO_PREVIOUS_CONNECTION = 0;
/**
* Constructor that uses the default location for the persistent adb keystore.
* Create an AdbKeyStore instance.
*
* <p>Upon creation, we parse {@link #mTempKeysFile} to determine authorized WiFi APs and
* retrieve the map of stored ADB keys and their last connected times. After that, we read
* the {@link #mUserKeyFile}, and any keys that exist in that file that do not exist in the
* map are added to the map (for backwards compatibility).
*/
AdbKeyStore() {
init();
}
/**
* Constructor that uses the specified file as the location for the persistent adb keystore.
*/
AdbKeyStore(File keyFile) {
mKeyFile = keyFile;
init();
}
private void init() {
initKeyFile();
mKeyMap = getKeyMap();
mTrustedNetworks = getTrustedNetworks();
readTempKeysFile();
mSystemKeys = getSystemKeysFromFile(SYSTEM_KEY_FILE);
addUserKeysToKeyStore();
addExistingUserKeysToKeyStore();
}
public void reloadKeyMap() {
readTempKeysFile();
}
public void addTrustedNetwork(String bssid) {
@@ -1877,7 +1880,6 @@ public class AdbDebuggingManager {
public void removeKey(String key) {
if (mKeyMap.containsKey(key)) {
mKeyMap.remove(key);
writeKeys(mKeyMap.keySet());
sendPersistKeyStoreMessage();
}
}
@@ -1886,12 +1888,9 @@ public class AdbDebuggingManager {
* Initializes the key file that will be used to persist the adb grants.
*/
private void initKeyFile() {
if (mKeyFile == null) {
mKeyFile = getAdbTempKeysFile();
}
// getAdbTempKeysFile can return null if the adb file cannot be obtained
if (mKeyFile != null) {
mAtomicKeyFile = new AtomicFile(mKeyFile);
// mTempKeysFile can be null if the adb file cannot be obtained
if (mTempKeysFile != null) {
mAtomicKeyFile = new AtomicFile(mTempKeysFile);
}
}
@@ -1932,201 +1931,108 @@ public class AdbDebuggingManager {
}
/**
* Returns the key map with the keys and last connection times from the key file.
* Update the key map and the trusted networks list with values parsed from the temp keys
* file.
*/
private Map<String, Long> getKeyMap() {
Map<String, Long> keyMap = new HashMap<String, Long>();
// if the AtomicFile could not be instantiated before attempt again; if it still fails
// return an empty key map.
private void readTempKeysFile() {
mKeyMap.clear();
mTrustedNetworks.clear();
if (mAtomicKeyFile == null) {
initKeyFile();
if (mAtomicKeyFile == null) {
Slog.e(TAG, "Unable to obtain the key file, " + mKeyFile + ", for reading");
return keyMap;
Slog.e(
TAG,
"Unable to obtain the key file, " + mTempKeysFile + ", for reading");
return;
}
}
if (!mAtomicKeyFile.exists()) {
return keyMap;
return;
}
try (FileInputStream keyStream = mAtomicKeyFile.openRead()) {
TypedXmlPullParser parser = Xml.resolvePullParser(keyStream);
// Check for supported keystore version.
XmlUtils.beginDocument(parser, XML_KEYSTORE_START_TAG);
if (parser.next() != XmlPullParser.END_DOCUMENT) {
String tagName = parser.getName();
if (tagName == null || !XML_KEYSTORE_START_TAG.equals(tagName)) {
Slog.e(TAG, "Expected " + XML_KEYSTORE_START_TAG + ", but got tag="
+ tagName);
return keyMap;
}
TypedXmlPullParser parser;
try {
parser = Xml.resolvePullParser(keyStream);
XmlUtils.beginDocument(parser, XML_KEYSTORE_START_TAG);
int keystoreVersion = parser.getAttributeInt(null, XML_ATTRIBUTE_VERSION);
if (keystoreVersion > MAX_SUPPORTED_KEYSTORE_VERSION) {
Slog.e(TAG, "Keystore version=" + keystoreVersion
+ " not supported (max_supported="
+ MAX_SUPPORTED_KEYSTORE_VERSION + ")");
return keyMap;
return;
}
} catch (XmlPullParserException e) {
// This could be because the XML document doesn't start with
// XML_KEYSTORE_START_TAG. Try again, instead just starting the document with
// the adbKey tag (the old format).
parser = Xml.resolvePullParser(keyStream);
}
while (parser.next() != XmlPullParser.END_DOCUMENT) {
String tagName = parser.getName();
if (tagName == null) {
break;
} else if (!tagName.equals(XML_TAG_ADB_KEY)) {
XmlUtils.skipCurrentTag(parser);
continue;
}
String key = parser.getAttributeValue(null, XML_ATTRIBUTE_KEY);
long connectionTime;
try {
connectionTime = parser.getAttributeLong(null,
XML_ATTRIBUTE_LAST_CONNECTION);
} catch (XmlPullParserException e) {
Slog.e(TAG,
"Caught a NumberFormatException parsing the last connection time: "
+ e);
XmlUtils.skipCurrentTag(parser);
continue;
}
keyMap.put(key, connectionTime);
}
readKeyStoreContents(parser);
} catch (IOException e) {
Slog.e(TAG, "Caught an IOException parsing the XML key file: ", e);
} catch (XmlPullParserException e) {
Slog.w(TAG, "Caught XmlPullParserException parsing the XML key file: ", e);
// The file could be written in a format prior to introducing keystore tag.
return getKeyMapBeforeKeystoreVersion();
Slog.e(TAG, "Caught XmlPullParserException parsing the XML key file: ", e);
}
return keyMap;
}
/**
* Returns the key map with the keys and last connection times from the key file.
* This implementation was prior to adding the XML_KEYSTORE_START_TAG.
*/
private Map<String, Long> getKeyMapBeforeKeystoreVersion() {
Map<String, Long> keyMap = new HashMap<String, Long>();
// if the AtomicFile could not be instantiated before attempt again; if it still fails
// return an empty key map.
if (mAtomicKeyFile == null) {
initKeyFile();
if (mAtomicKeyFile == null) {
Slog.e(TAG, "Unable to obtain the key file, " + mKeyFile + ", for reading");
return keyMap;
private void readKeyStoreContents(TypedXmlPullParser parser)
throws XmlPullParserException, IOException {
// This parser is very forgiving. For backwards-compatibility, we simply iterate through
// all the tags in the file, skipping over anything that's not an <adbKey> tag or a
// <wifiAP> tag. Invalid tags (such as ones that don't have a valid "lastConnection"
// attribute) are simply ignored.
while ((parser.next()) != XmlPullParser.END_DOCUMENT) {
String tagName = parser.getName();
if (XML_TAG_ADB_KEY.equals(tagName)) {
addAdbKeyToKeyMap(parser);
} else if (XML_TAG_WIFI_ACCESS_POINT.equals(tagName)) {
addTrustedNetworkToTrustedNetworks(parser);
} else {
Slog.w(TAG, "Ignoring tag '" + tagName + "'. Not recognized.");
}
XmlUtils.skipCurrentTag(parser);
}
if (!mAtomicKeyFile.exists()) {
return keyMap;
}
try (FileInputStream keyStream = mAtomicKeyFile.openRead()) {
TypedXmlPullParser parser = Xml.resolvePullParser(keyStream);
XmlUtils.beginDocument(parser, XML_TAG_ADB_KEY);
while (parser.next() != XmlPullParser.END_DOCUMENT) {
String tagName = parser.getName();
if (tagName == null) {
break;
} else if (!tagName.equals(XML_TAG_ADB_KEY)) {
XmlUtils.skipCurrentTag(parser);
continue;
}
String key = parser.getAttributeValue(null, XML_ATTRIBUTE_KEY);
long connectionTime;
try {
connectionTime = parser.getAttributeLong(null,
XML_ATTRIBUTE_LAST_CONNECTION);
} catch (XmlPullParserException e) {
Slog.e(TAG,
"Caught a NumberFormatException parsing the last connection time: "
+ e);
XmlUtils.skipCurrentTag(parser);
continue;
}
keyMap.put(key, connectionTime);
}
} catch (IOException | XmlPullParserException e) {
Slog.e(TAG, "Caught an exception parsing the XML key file: ", e);
}
return keyMap;
}
/**
* Returns the map of trusted networks from the keystore file.
*
* This was implemented in keystore version 1.
*/
private List<String> getTrustedNetworks() {
List<String> trustedNetworks = new ArrayList<String>();
// if the AtomicFile could not be instantiated before attempt again; if it still fails
// return an empty key map.
if (mAtomicKeyFile == null) {
initKeyFile();
if (mAtomicKeyFile == null) {
Slog.e(TAG, "Unable to obtain the key file, " + mKeyFile + ", for reading");
return trustedNetworks;
}
private void addAdbKeyToKeyMap(TypedXmlPullParser parser) {
String key = parser.getAttributeValue(null, XML_ATTRIBUTE_KEY);
try {
long connectionTime =
parser.getAttributeLong(null, XML_ATTRIBUTE_LAST_CONNECTION);
mKeyMap.put(key, connectionTime);
} catch (XmlPullParserException e) {
Slog.e(TAG, "Error reading adbKey attributes", e);
}
if (!mAtomicKeyFile.exists()) {
return trustedNetworks;
}
try (FileInputStream keyStream = mAtomicKeyFile.openRead()) {
TypedXmlPullParser parser = Xml.resolvePullParser(keyStream);
// Check for supported keystore version.
XmlUtils.beginDocument(parser, XML_KEYSTORE_START_TAG);
if (parser.next() != XmlPullParser.END_DOCUMENT) {
String tagName = parser.getName();
if (tagName == null || !XML_KEYSTORE_START_TAG.equals(tagName)) {
Slog.e(TAG, "Expected " + XML_KEYSTORE_START_TAG + ", but got tag="
+ tagName);
return trustedNetworks;
}
int keystoreVersion = parser.getAttributeInt(null, XML_ATTRIBUTE_VERSION);
if (keystoreVersion > MAX_SUPPORTED_KEYSTORE_VERSION) {
Slog.e(TAG, "Keystore version=" + keystoreVersion
+ " not supported (max_supported="
+ MAX_SUPPORTED_KEYSTORE_VERSION);
return trustedNetworks;
}
}
while (parser.next() != XmlPullParser.END_DOCUMENT) {
String tagName = parser.getName();
if (tagName == null) {
break;
} else if (!tagName.equals(XML_TAG_WIFI_ACCESS_POINT)) {
XmlUtils.skipCurrentTag(parser);
continue;
}
String bssid = parser.getAttributeValue(null, XML_ATTRIBUTE_WIFI_BSSID);
trustedNetworks.add(bssid);
}
} catch (IOException | XmlPullParserException | NumberFormatException e) {
Slog.e(TAG, "Caught an exception parsing the XML key file: ", e);
}
return trustedNetworks;
}
private void addTrustedNetworkToTrustedNetworks(TypedXmlPullParser parser) {
String bssid = parser.getAttributeValue(null, XML_ATTRIBUTE_WIFI_BSSID);
mTrustedNetworks.add(bssid);
}
/**
* Updates the keystore with keys that were previously set to be always allowed before the
* connection time of keys was tracked.
*/
private void addUserKeysToKeyStore() {
File userKeyFile = getUserKeyFile();
private void addExistingUserKeysToKeyStore() {
if (mUserKeyFile == null || !mUserKeyFile.exists()) {
return;
}
boolean mapUpdated = false;
if (userKeyFile != null && userKeyFile.exists()) {
try (BufferedReader in = new BufferedReader(new FileReader(userKeyFile))) {
long time = System.currentTimeMillis();
String key;
while ((key = in.readLine()) != null) {
// if the keystore does not contain the key from the user key file then add
// it to the Map with the current system time to prevent it from expiring
// immediately if the user is actively using this key.
if (!mKeyMap.containsKey(key)) {
mKeyMap.put(key, time);
mapUpdated = true;
}
try (BufferedReader in = new BufferedReader(new FileReader(mUserKeyFile))) {
String key;
while ((key = in.readLine()) != null) {
// if the keystore does not contain the key from the user key file then add
// it to the Map with the current system time to prevent it from expiring
// immediately if the user is actively using this key.
if (!mKeyMap.containsKey(key)) {
mKeyMap.put(key, mTicker.currentTimeMillis());
mapUpdated = true;
}
} catch (IOException e) {
Slog.e(TAG, "Caught an exception reading " + userKeyFile + ": " + e);
}
} catch (IOException e) {
Slog.e(TAG, "Caught an exception reading " + mUserKeyFile + ": " + e);
}
if (mapUpdated) {
sendPersistKeyStoreMessage();
@@ -2147,7 +2053,9 @@ public class AdbDebuggingManager {
if (mAtomicKeyFile == null) {
initKeyFile();
if (mAtomicKeyFile == null) {
Slog.e(TAG, "Unable to obtain the key file, " + mKeyFile + ", for writing");
Slog.e(
TAG,
"Unable to obtain the key file, " + mTempKeysFile + ", for writing");
return;
}
}
@@ -2178,17 +2086,21 @@ public class AdbDebuggingManager {
Slog.e(TAG, "Caught an exception writing the key map: ", e);
mAtomicKeyFile.failWrite(keyStream);
}
writeKeys(mKeyMap.keySet());
}
private boolean filterOutOldKeys() {
boolean keysDeleted = false;
long allowedTime = getAllowedConnectionTime();
long systemTime = System.currentTimeMillis();
if (allowedTime == 0) {
return false;
}
boolean keysDeleted = false;
long systemTime = mTicker.currentTimeMillis();
Iterator<Map.Entry<String, Long>> keyMapIterator = mKeyMap.entrySet().iterator();
while (keyMapIterator.hasNext()) {
Map.Entry<String, Long> keyEntry = keyMapIterator.next();
long connectionTime = keyEntry.getValue();
if (allowedTime != 0 && systemTime > (connectionTime + allowedTime)) {
if (systemTime > (connectionTime + allowedTime)) {
keyMapIterator.remove();
keysDeleted = true;
}
@@ -2212,7 +2124,7 @@ public class AdbDebuggingManager {
if (allowedTime == 0) {
return minExpiration;
}
long systemTime = System.currentTimeMillis();
long systemTime = mTicker.currentTimeMillis();
Iterator<Map.Entry<String, Long>> keyMapIterator = mKeyMap.entrySet().iterator();
while (keyMapIterator.hasNext()) {
Map.Entry<String, Long> keyEntry = keyMapIterator.next();
@@ -2233,7 +2145,9 @@ public class AdbDebuggingManager {
public void deleteKeyStore() {
mKeyMap.clear();
mTrustedNetworks.clear();
deleteKeyFile();
if (mUserKeyFile != null) {
mUserKeyFile.delete();
}
if (mAtomicKeyFile == null) {
return;
}
@@ -2260,7 +2174,8 @@ public class AdbDebuggingManager {
* is set to true the time will be set even if it is older than the previously written
* connection time.
*/
public void setLastConnectionTime(String key, long connectionTime, boolean force) {
@VisibleForTesting
void setLastConnectionTime(String key, long connectionTime, boolean force) {
// Do not set the connection time to a value that is earlier than what was previously
// stored as the last connection time unless force is set.
if (mKeyMap.containsKey(key) && mKeyMap.get(key) >= connectionTime && !force) {
@@ -2271,11 +2186,6 @@ public class AdbDebuggingManager {
if (mSystemKeys.contains(key)) {
return;
}
// if this is the first time the key is being added then write it to the key file as
// well.
if (!mKeyMap.containsKey(key)) {
writeKey(key);
}
mKeyMap.put(key, connectionTime);
}
@@ -2307,12 +2217,8 @@ public class AdbDebuggingManager {
long allowedConnectionTime = getAllowedConnectionTime();
// if the allowed connection time is 0 then revert to the previous behavior of always
// allowing previously granted adb grants.
if (allowedConnectionTime == 0 || (System.currentTimeMillis() < (lastConnectionTime
+ allowedConnectionTime))) {
return true;
} else {
return false;
}
return allowedConnectionTime == 0
|| (mTicker.currentTimeMillis() < (lastConnectionTime + allowedConnectionTime));
}
/**
@@ -2324,4 +2230,15 @@ public class AdbDebuggingManager {
return mTrustedNetworks.contains(bssid);
}
}
/**
* A Guava-like interface for getting the current system time.
*
* This allows us to swap a fake ticker in for testing to reduce "Thread.sleep()" calls and test
* for exact expected times instead of random ones.
*/
@VisibleForTesting
interface Ticker {
long currentTimeMillis();
}
}

View File

@@ -151,6 +151,14 @@ public class AdbService extends IAdbManager.Stub {
return mDebuggingManager == null ? null : mDebuggingManager.getAdbTempKeysFile();
}
@Override
public void notifyKeyFilesUpdated() {
if (mDebuggingManager == null) {
return;
}
mDebuggingManager.notifyKeyFilesUpdated();
}
@Override
public void startAdbdForTransport(byte transportType) {
FgThread.getHandler().sendMessage(obtainMessage(

View File

@@ -189,6 +189,7 @@ public class TestHarnessModeService extends SystemService {
if (adbManager.getAdbTempKeysFile() != null) {
writeBytesToFile(persistentData.mAdbTempKeys, adbManager.getAdbTempKeysFile().toPath());
}
adbManager.notifyKeyFilesUpdated();
}
private void configureUser() {

View File

@@ -36,8 +36,6 @@ import android.util.Log;
import androidx.test.InstrumentationRegistry;
import com.android.server.FgThread;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -48,6 +46,11 @@ import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
@@ -88,6 +91,7 @@ public final class AdbDebuggingManagerTest {
private long mOriginalAllowedConnectionTime;
private File mAdbKeyXmlFile;
private File mAdbKeyFile;
private FakeTicker mFakeTicker;
@Before
public void setUp() throws Exception {
@@ -96,14 +100,25 @@ public final class AdbDebuggingManagerTest {
if (mAdbKeyFile.exists()) {
mAdbKeyFile.delete();
}
mManager = new AdbDebuggingManager(mContext, ADB_CONFIRM_COMPONENT, mAdbKeyFile);
mAdbKeyXmlFile = new File(mContext.getFilesDir(), "test_adb_keys.xml");
if (mAdbKeyXmlFile.exists()) {
mAdbKeyXmlFile.delete();
}
mFakeTicker = new FakeTicker();
// Set the ticker time to October 22, 2008 (the day the T-Mobile G1 was released)
mFakeTicker.advance(1224658800L);
mThread = new AdbDebuggingThreadTest();
mKeyStore = mManager.new AdbKeyStore(mAdbKeyXmlFile);
mHandler = mManager.new AdbDebuggingHandler(FgThread.get().getLooper(), mThread, mKeyStore);
mManager = new AdbDebuggingManager(
mContext, ADB_CONFIRM_COMPONENT, mAdbKeyFile, mAdbKeyXmlFile, mThread, mFakeTicker);
mHandler = mManager.mHandler;
mThread.setHandler(mHandler);
mHandler.initKeyStore();
mKeyStore = mHandler.mAdbKeyStore;
mOriginalAllowedConnectionTime = mKeyStore.getAllowedConnectionTime();
mBlockingQueue = new ArrayBlockingQueue<>(1);
}
@@ -122,7 +137,7 @@ public final class AdbDebuggingManagerTest {
private void setAllowedConnectionTime(long connectionTime) {
Settings.Global.putLong(mContext.getContentResolver(),
Settings.Global.ADB_ALLOWED_CONNECTION_TIME, connectionTime);
};
}
@Test
public void testAllowNewKeyOnce() throws Exception {
@@ -158,20 +173,15 @@ public final class AdbDebuggingManagerTest {
// Allow a connection from a new key with the 'Always allow' option selected.
runAdbTest(TEST_KEY_1, true, true, false);
// Get the last connection time for the currently connected key to verify that it is updated
// after the disconnect.
long lastConnectionTime = mKeyStore.getLastConnectionTime(TEST_KEY_1);
// Sleep for a small amount of time to ensure a difference can be observed in the last
// connection time after a disconnect.
Thread.sleep(10);
// Advance the clock by 10ms to ensure there's a difference
mFakeTicker.advance(10 * 1_000_000);
// Send the disconnect message for the currently connected key to trigger an update of the
// last connection time.
disconnectKey(TEST_KEY_1);
assertNotEquals(
assertEquals(
"The last connection time was not updated after the disconnect",
lastConnectionTime,
mFakeTicker.currentTimeMillis(),
mKeyStore.getLastConnectionTime(TEST_KEY_1));
}
@@ -244,8 +254,8 @@ public final class AdbDebuggingManagerTest {
// Get the current last connection time for comparison after the scheduled job is run
long lastConnectionTime = mKeyStore.getLastConnectionTime(TEST_KEY_1);
// Sleep a small amount of time to ensure that the updated connection time changes
Thread.sleep(10);
// Advance a small amount of time to ensure that the updated connection time changes
mFakeTicker.advance(10);
// Send a message to the handler to update the last connection time for the active key
updateKeyStore();
@@ -269,13 +279,13 @@ public final class AdbDebuggingManagerTest {
persistKeyStore();
assertTrue(
"The key with the 'Always allow' option selected was not persisted in the keystore",
mManager.new AdbKeyStore(mAdbKeyXmlFile).isKeyAuthorized(TEST_KEY_1));
mManager.new AdbKeyStore().isKeyAuthorized(TEST_KEY_1));
// Get the current last connection time to ensure it is updated in the persisted keystore.
long lastConnectionTime = mKeyStore.getLastConnectionTime(TEST_KEY_1);
// Sleep a small amount of time to ensure the last connection time is updated.
Thread.sleep(10);
// Advance a small amount of time to ensure the last connection time is updated.
mFakeTicker.advance(10);
// Send a message to the handler to update the last connection time for the active key.
updateKeyStore();
@@ -286,7 +296,7 @@ public final class AdbDebuggingManagerTest {
assertNotEquals(
"The last connection time in the key file was not updated after the update "
+ "connection time message", lastConnectionTime,
mManager.new AdbKeyStore(mAdbKeyXmlFile).getLastConnectionTime(TEST_KEY_1));
mManager.new AdbKeyStore().getLastConnectionTime(TEST_KEY_1));
// Verify that the key is in the adb_keys file
assertTrue("The key was not in the adb_keys file after persisting the keystore",
isKeyInFile(TEST_KEY_1, mAdbKeyFile));
@@ -327,8 +337,8 @@ public final class AdbDebuggingManagerTest {
// Set the allowed window to a small value to ensure the time is beyond the allowed window.
setAllowedConnectionTime(1);
// Sleep for a small amount of time to exceed the allowed window.
Thread.sleep(10);
// Advance a small amount of time to exceed the allowed window.
mFakeTicker.advance(10);
// The AdbKeyStore has a method to get the time of the next key expiration to ensure the
// scheduled job runs at the time of the next expiration or after 24 hours, whichever occurs
@@ -478,9 +488,12 @@ public final class AdbDebuggingManagerTest {
// Set the current expiration time to a minute from expiration and verify this new value is
// returned.
final long newExpirationTime = 60000;
mKeyStore.setLastConnectionTime(TEST_KEY_1,
System.currentTimeMillis() - Settings.Global.DEFAULT_ADB_ALLOWED_CONNECTION_TIME
+ newExpirationTime, true);
mKeyStore.setLastConnectionTime(
TEST_KEY_1,
mFakeTicker.currentTimeMillis()
- Settings.Global.DEFAULT_ADB_ALLOWED_CONNECTION_TIME
+ newExpirationTime,
true);
expirationTime = mKeyStore.getNextExpirationTime();
if (Math.abs(expirationTime - newExpirationTime) > epsilon) {
fail("The expiration time for a key about to expire, " + expirationTime
@@ -525,7 +538,7 @@ public final class AdbDebuggingManagerTest {
// Get the last connection time for the key to verify that it is updated when the connected
// key message is sent.
long connectionTime = mKeyStore.getLastConnectionTime(TEST_KEY_1);
Thread.sleep(10);
mFakeTicker.advance(10);
mHandler.obtainMessage(AdbDebuggingManager.AdbDebuggingHandler.MESSAGE_ADB_CONNECTED_KEY,
TEST_KEY_1).sendToTarget();
flushHandlerQueue();
@@ -536,7 +549,7 @@ public final class AdbDebuggingManagerTest {
// Verify that the scheduled job updates the connection time of the key.
connectionTime = mKeyStore.getLastConnectionTime(TEST_KEY_1);
Thread.sleep(10);
mFakeTicker.advance(10);
updateKeyStore();
assertNotEquals(
"The connection time for the key must be updated when the update keystore message"
@@ -545,7 +558,7 @@ public final class AdbDebuggingManagerTest {
// Verify that the connection time is updated when the key is disconnected.
connectionTime = mKeyStore.getLastConnectionTime(TEST_KEY_1);
Thread.sleep(10);
mFakeTicker.advance(10);
disconnectKey(TEST_KEY_1);
assertNotEquals(
"The connection time for the key must be updated when the disconnected message is"
@@ -628,11 +641,11 @@ public final class AdbDebuggingManagerTest {
setAllowedConnectionTime(Settings.Global.DEFAULT_ADB_ALLOWED_CONNECTION_TIME);
// The untracked keys should be added to the keystore as part of the constructor.
AdbDebuggingManager.AdbKeyStore adbKeyStore = mManager.new AdbKeyStore(mAdbKeyXmlFile);
AdbDebuggingManager.AdbKeyStore adbKeyStore = mManager.new AdbKeyStore();
// Verify that the connection time for each test key is within a small value of the current
// time.
long time = System.currentTimeMillis();
long time = mFakeTicker.currentTimeMillis();
for (String key : testKeys) {
long connectionTime = adbKeyStore.getLastConnectionTime(key);
if (Math.abs(connectionTime - connectionTime) > epsilon) {
@@ -651,11 +664,11 @@ public final class AdbDebuggingManagerTest {
runAdbTest(TEST_KEY_1, true, true, false);
runAdbTest(TEST_KEY_2, true, true, false);
// Sleep a small amount of time to ensure the connection time is updated by the scheduled
// Advance a small amount of time to ensure the connection time is updated by the scheduled
// job.
long connectionTime1 = mKeyStore.getLastConnectionTime(TEST_KEY_1);
long connectionTime2 = mKeyStore.getLastConnectionTime(TEST_KEY_2);
Thread.sleep(10);
mFakeTicker.advance(10);
updateKeyStore();
assertNotEquals(
"The connection time for test key 1 must be updated after the scheduled job runs",
@@ -669,7 +682,7 @@ public final class AdbDebuggingManagerTest {
disconnectKey(TEST_KEY_2);
connectionTime1 = mKeyStore.getLastConnectionTime(TEST_KEY_1);
connectionTime2 = mKeyStore.getLastConnectionTime(TEST_KEY_2);
Thread.sleep(10);
mFakeTicker.advance(10);
updateKeyStore();
assertNotEquals(
"The connection time for test key 1 must be updated after another key is "
@@ -686,8 +699,6 @@ public final class AdbDebuggingManagerTest {
// to clear the adb authorizations when adb is disabled after a boot a NullPointerException
// was thrown as deleteKeyStore is invoked against the key store. This test ensures the
// key store can be successfully cleared when adb is disabled.
mHandler = mManager.new AdbDebuggingHandler(FgThread.get().getLooper());
clearKeyStore();
}
@@ -723,12 +734,104 @@ public final class AdbDebuggingManagerTest {
// Now remove one of the keys and make sure the other key is still there
mKeyStore.removeKey(TEST_KEY_1);
// Wait for the handler queue to receive the MESSAGE_ADB_PERSIST_KEYSTORE
flushHandlerQueue();
assertFalse("The key was still in the adb_keys file after removing the key",
isKeyInFile(TEST_KEY_1, mAdbKeyFile));
assertTrue("The key was not in the adb_keys file after removing a different key",
isKeyInFile(TEST_KEY_2, mAdbKeyFile));
}
@Test
public void testAdbKeyStore_addDuplicateKey_doesNotAddDuplicateToAdbKeyFile() throws Exception {
setAllowedConnectionTime(0);
runAdbTest(TEST_KEY_1, true, true, false);
persistKeyStore();
runAdbTest(TEST_KEY_1, true, true, false);
persistKeyStore();
assertEquals("adb_keys contains duplicate keys", 1, adbKeyFileKeys(mAdbKeyFile).size());
}
@Test
public void testAdbKeyStore_adbTempKeysFile_readsLastConnectionTimeFromXml() throws Exception {
long insertTime = mFakeTicker.currentTimeMillis();
runAdbTest(TEST_KEY_1, true, true, false);
persistKeyStore();
mFakeTicker.advance(10);
AdbDebuggingManager.AdbKeyStore newKeyStore = mManager.new AdbKeyStore();
assertEquals(
"KeyStore not populated from the XML file.",
insertTime,
newKeyStore.getLastConnectionTime(TEST_KEY_1));
}
@Test
public void test_notifyKeyFilesUpdated_filesDeletedRemovesPreviouslyAddedKey()
throws Exception {
runAdbTest(TEST_KEY_1, true, true, false);
persistKeyStore();
Files.delete(mAdbKeyXmlFile.toPath());
Files.delete(mAdbKeyFile.toPath());
mManager.notifyKeyFilesUpdated();
flushHandlerQueue();
assertFalse(
"Key is authorized after reloading deleted key files. Was state preserved?",
mKeyStore.isKeyAuthorized(TEST_KEY_1));
}
@Test
public void test_notifyKeyFilesUpdated_newKeyIsAuthorized() throws Exception {
runAdbTest(TEST_KEY_1, true, true, false);
persistKeyStore();
// Back up the existing key files
Path tempXmlFile = Files.createTempFile("adbKeyXmlFile", ".tmp");
Path tempAdbKeysFile = Files.createTempFile("adb_keys", ".tmp");
Files.copy(mAdbKeyXmlFile.toPath(), tempXmlFile, StandardCopyOption.REPLACE_EXISTING);
Files.copy(mAdbKeyFile.toPath(), tempAdbKeysFile, StandardCopyOption.REPLACE_EXISTING);
// Delete the existing key files
Files.delete(mAdbKeyXmlFile.toPath());
Files.delete(mAdbKeyFile.toPath());
// Notify the manager that adb key files have changed.
mManager.notifyKeyFilesUpdated();
flushHandlerQueue();
// Copy the files back
Files.copy(tempXmlFile, mAdbKeyXmlFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
Files.copy(tempAdbKeysFile, mAdbKeyFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
// Tell the manager that the key files have changed.
mManager.notifyKeyFilesUpdated();
flushHandlerQueue();
assertTrue(
"Key is not authorized after reloading key files.",
mKeyStore.isKeyAuthorized(TEST_KEY_1));
}
@Test
public void testAdbKeyStore_adbWifiConnect_storesBssidWhenAlwaysAllow() throws Exception {
String trustedNetwork = "My Network";
mKeyStore.addTrustedNetwork(trustedNetwork);
persistKeyStore();
AdbDebuggingManager.AdbKeyStore newKeyStore = mManager.new AdbKeyStore();
assertTrue(
"Persisted trusted network not found in new keystore instance.",
newKeyStore.isTrustedNetwork(trustedNetwork));
}
@Test
public void testIsValidMdnsServiceName() {
// Longer than 15 characters
@@ -1030,28 +1133,27 @@ public final class AdbDebuggingManagerTest {
if (key == null) {
return false;
}
return adbKeyFileKeys(keyFile).contains(key);
}
private static List<String> adbKeyFileKeys(File keyFile) throws Exception {
List<String> keys = new ArrayList<>();
if (keyFile.exists()) {
try (BufferedReader in = new BufferedReader(new FileReader(keyFile))) {
String currKey;
while ((currKey = in.readLine()) != null) {
if (key.equals(currKey)) {
return true;
}
keys.add(currKey);
}
}
}
return false;
return keys;
}
/**
* Helper class that extends AdbDebuggingThread to receive the response from AdbDebuggingManager
* indicating whether the key should be allowed to connect.
*/
class AdbDebuggingThreadTest extends AdbDebuggingManager.AdbDebuggingThread {
AdbDebuggingThreadTest() {
mManager.super();
}
private class AdbDebuggingThreadTest extends AdbDebuggingManager.AdbDebuggingThread {
@Override
public void sendResponse(String msg) {
TestResult result = new TestResult(TestResult.RESULT_RESPONSE_RECEIVED, msg);
@@ -1091,4 +1193,17 @@ public final class AdbDebuggingManagerTest {
return "{mReturnCode = " + mReturnCode + ", mMessage = " + mMessage + "}";
}
}
private static class FakeTicker implements AdbDebuggingManager.Ticker {
private long mCurrentTime;
private void advance(long milliseconds) {
mCurrentTime += milliseconds;
}
@Override
public long currentTimeMillis() {
return mCurrentTime;
}
}
}