diff options
-rw-r--r-- | proto/src/system_messages.proto | 4 | ||||
-rw-r--r-- | services/core/java/com/android/server/adb/AdbDebuggingManager.java | 1267 | ||||
-rw-r--r-- | services/core/java/com/android/server/adb/AdbService.java | 146 | ||||
-rw-r--r-- | services/core/jni/Android.bp | 3 | ||||
-rw-r--r-- | services/core/jni/com_android_server_adb_AdbDebuggingManager.cpp | 168 | ||||
-rw-r--r-- | services/core/jni/onload.cpp | 2 | ||||
-rw-r--r-- | services/usb/java/com/android/server/usb/UsbDeviceManager.java | 27 |
7 files changed, 1517 insertions, 100 deletions
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto index cff55046fc2b..c5a5d34ff30a 100644 --- a/proto/src/system_messages.proto +++ b/proto/src/system_messages.proto @@ -255,6 +255,10 @@ message SystemMessage { // Inform the user a foreground service while-in-use permission is restricted. NOTE_FOREGROUND_SERVICE_WHILE_IN_USE_PERMISSION = 61; + // Display the Android Debug Protocol status + // Package: android + NOTE_ADB_WIFI_ACTIVE = 62; + // ADD_NEW_IDS_ABOVE_THIS_LINE // Legacy IDs with arbitrary values appear below // Legacy IDs existed as stable non-conflicting constants prior to the O release diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java index f16e3ce11622..6c68c312bbde 100644 --- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java +++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java @@ -20,19 +20,36 @@ import static com.android.internal.util.dump.DumpUtils.writeStringIfNotNull; import android.annotation.TestApi; import android.app.ActivityManager; +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; import android.content.ActivityNotFoundException; +import android.content.BroadcastReceiver; import android.content.ComponentName; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.content.res.Resources; import android.database.ContentObserver; +import android.debug.AdbManager; import android.debug.AdbProtoEnums; import android.debug.AdbTransportType; +import android.debug.PairDevice; +import android.net.ConnectivityManager; import android.net.LocalSocket; import android.net.LocalSocketAddress; +import android.net.NetworkInfo; import android.net.Uri; +import android.net.nsd.NsdManager; +import android.net.nsd.NsdServiceInfo; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; +import android.os.Bundle; import android.os.Environment; import android.os.FileUtils; import android.os.Handler; @@ -51,6 +68,8 @@ import android.util.Xml; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; +import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.XmlUtils; @@ -71,6 +90,8 @@ import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; +import java.security.SecureRandom; +import java.util.AbstractMap; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -79,6 +100,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; 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 @@ -87,6 +109,7 @@ import java.util.Set; public class AdbDebuggingManager { private static final String TAG = "AdbDebuggingManager"; private static final boolean DEBUG = false; + private static final boolean MDNS_DEBUG = false; private static final String ADBD_SOCKET = "adbd"; private static final String ADB_DIRECTORY = "misc/adb"; @@ -98,19 +121,39 @@ public class AdbDebuggingManager { private static final int BUFFER_SIZE = 65536; private final Context mContext; + private final ContentResolver mContentResolver; private final Handler mHandler; private AdbDebuggingThread mThread; - private boolean mAdbEnabled = false; + private boolean mAdbUsbEnabled = false; + private boolean mAdbWifiEnabled = false; private String mFingerprints; - private final List<String> mConnectedKeys; + // 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 static final String WIFI_PERSISTENT_CONFIG_PROPERTY = + "persist.adb.tls_server.enable"; + private static final String WIFI_PERSISTENT_GUID = + "persist.adb.wifi.guid"; + private static final int PAIRING_CODE_LENGTH = 6; + private PairingThread mPairingThread = null; + // A list of keys connected via wifi + private final Set<String> mWifiConnectedKeys; + // The current info of the adbwifi connection. + private AdbConnectionInfo mAdbConnectionInfo; + // Polls for a tls port property when adb wifi is enabled + private AdbConnectionPortPoller mConnectionPortPoller; + private final PortListenerImpl mPortListener = new PortListenerImpl(); + public AdbDebuggingManager(Context context) { mHandler = new AdbDebuggingHandler(FgThread.get().getLooper()); mContext = context; + mContentResolver = mContext.getContentResolver(); mTestUserKeyFile = null; - mConnectedKeys = new ArrayList<>(1); + mConnectedKeys = new HashMap<String, Integer>(); + mWifiConnectedKeys = new HashSet<String>(); + mAdbConnectionInfo = new AdbConnectionInfo(); } /** @@ -121,9 +164,178 @@ public class AdbDebuggingManager { protected AdbDebuggingManager(Context context, String confirmComponent, File testUserKeyFile) { mHandler = new AdbDebuggingHandler(FgThread.get().getLooper()); mContext = context; + mContentResolver = mContext.getContentResolver(); mConfirmComponent = confirmComponent; mTestUserKeyFile = testUserKeyFile; - mConnectedKeys = new ArrayList<>(); + mConnectedKeys = new HashMap<String, Integer>(); + mWifiConnectedKeys = new HashSet<String>(); + mAdbConnectionInfo = new AdbConnectionInfo(); + } + + class PairingThread extends Thread implements NsdManager.RegistrationListener { + private NsdManager mNsdManager; + private String mPublicKey; + private String mPairingCode; + private String mGuid; + private String mServiceName; + private final String mServiceType = "_adb_secure_pairing._tcp."; + private int mPort; + + private native int native_pairing_start(String guid, String password); + private native void native_pairing_cancel(); + private native boolean native_pairing_wait(); + + PairingThread(String pairingCode, String serviceName) { + super(TAG); + mPairingCode = pairingCode; + mGuid = SystemProperties.get(WIFI_PERSISTENT_GUID); + mServiceName = serviceName; + if (serviceName == null || serviceName.isEmpty()) { + mServiceName = mGuid; + } + mPort = -1; + mNsdManager = (NsdManager) mContext.getSystemService(Context.NSD_SERVICE); + } + + @Override + public void run() { + if (mGuid.isEmpty()) { + Slog.e(TAG, "adbwifi guid was not set"); + return; + } + mPort = native_pairing_start(mGuid, mPairingCode); + if (mPort <= 0 || mPort > 65535) { + Slog.e(TAG, "Unable to start pairing server"); + return; + } + + // Register the mdns service + NsdServiceInfo serviceInfo = new NsdServiceInfo(); + serviceInfo.setServiceName(mServiceName); + serviceInfo.setServiceType(mServiceType); + serviceInfo.setPort(mPort); + mNsdManager.registerService(serviceInfo, NsdManager.PROTOCOL_DNS_SD, this); + + // Send pairing port to UI + Message msg = mHandler.obtainMessage( + AdbDebuggingHandler.MSG_RESPONSE_PAIRING_PORT); + msg.obj = mPort; + mHandler.sendMessage(msg); + + boolean paired = native_pairing_wait(); + if (DEBUG) { + if (mPublicKey != null) { + Slog.i(TAG, "Pairing succeeded key=" + mPublicKey); + } else { + Slog.i(TAG, "Pairing failed"); + } + } + + mNsdManager.unregisterService(this); + + Bundle bundle = new Bundle(); + bundle.putString("publicKey", paired ? mPublicKey : null); + Message message = Message.obtain(mHandler, + AdbDebuggingHandler.MSG_RESPONSE_PAIRING_RESULT, + bundle); + mHandler.sendMessage(message); + } + + public void cancelPairing() { + native_pairing_cancel(); + } + + @Override + public void onServiceRegistered(NsdServiceInfo serviceInfo) { + if (MDNS_DEBUG) Slog.i(TAG, "Registered pairing service: " + serviceInfo); + } + + @Override + public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { + Slog.e(TAG, "Failed to register pairing service(err=" + errorCode + + "): " + serviceInfo); + cancelPairing(); + } + + @Override + public void onServiceUnregistered(NsdServiceInfo serviceInfo) { + if (MDNS_DEBUG) Slog.i(TAG, "Unregistered pairing service: " + serviceInfo); + } + + @Override + public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { + Slog.w(TAG, "Failed to unregister pairing service(err=" + errorCode + + "): " + serviceInfo); + } + } + + interface AdbConnectionPortListener { + void onPortReceived(int port); + } + + /** + * This class will poll for a period of time for adbd to write the port + * it connected to. + * + * TODO(joshuaduong): The port is being sent via system property because the adbd socket + * (AdbDebuggingManager) is not created when ro.adb.secure=0. Thus, we must communicate the + * port through different means. A better fix would be to always start AdbDebuggingManager, but + * it needs to adjust accordingly on whether ro.adb.secure is set. + */ + static class AdbConnectionPortPoller extends Thread { + private final String mAdbPortProp = "service.adb.tls.port"; + private AdbConnectionPortListener mListener; + private final int mDurationSecs = 10; + private AtomicBoolean mCanceled = new AtomicBoolean(false); + + AdbConnectionPortPoller(AdbConnectionPortListener listener) { + mListener = listener; + } + + @Override + public void run() { + if (DEBUG) Slog.d(TAG, "Starting adb port property poller"); + // Once adbwifi is enabled, we poll the service.adb.tls.port + // system property until we get the port, or -1 on failure. + // Let's also limit the polling to 10 seconds, just in case + // something went wrong. + for (int i = 0; i < mDurationSecs; ++i) { + if (mCanceled.get()) { + return; + } + + // If the property is set to -1, then that means adbd has failed + // to start the server. Otherwise we should have a valid port. + int port = SystemProperties.getInt(mAdbPortProp, Integer.MAX_VALUE); + if (port == -1 || (port > 0 && port <= 65535)) { + mListener.onPortReceived(port); + return; + } + SystemClock.sleep(1000); + } + Slog.w(TAG, "Failed to receive adb connection port"); + mListener.onPortReceived(-1); + } + + public void cancelAndWait() { + mCanceled.set(true); + if (this.isAlive()) { + try { + this.join(); + } catch (InterruptedException e) { + } + } + } + } + + class PortListenerImpl implements AdbConnectionPortListener { + public void onPortReceived(int port) { + Message msg = mHandler.obtainMessage(port > 0 + ? AdbDebuggingHandler.MSG_SERVER_CONNECTED + : AdbDebuggingHandler.MSG_SERVER_DISCONNECTED); + msg.obj = port; + mHandler.sendMessage(msg); + } } class AdbDebuggingThread extends Thread { @@ -213,6 +425,46 @@ public class AdbDebuggingManager { AdbDebuggingHandler.MESSAGE_ADB_CONNECTED_KEY); msg.obj = key; mHandler.sendMessage(msg); + } else if (buffer[0] == 'W' && buffer[1] == 'E') { + // adbd_auth.h and AdbTransportType.aidl need to be kept in + // sync. + byte transportType = buffer[2]; + String key = new String(Arrays.copyOfRange(buffer, 3, count)); + if (transportType == AdbTransportType.USB) { + Slog.d(TAG, "Received USB TLS connected key message: " + key); + Message msg = mHandler.obtainMessage( + AdbDebuggingHandler.MESSAGE_ADB_CONNECTED_KEY); + msg.obj = key; + mHandler.sendMessage(msg); + } else if (transportType == AdbTransportType.WIFI) { + Slog.d(TAG, "Received WIFI TLS connected key message: " + key); + Message msg = mHandler.obtainMessage( + AdbDebuggingHandler.MSG_WIFI_DEVICE_CONNECTED); + msg.obj = key; + mHandler.sendMessage(msg); + } else { + Slog.e(TAG, "Got unknown transport type from adbd (" + transportType + + ")"); + } + } else if (buffer[0] == 'W' && buffer[1] == 'F') { + byte transportType = buffer[2]; + String key = new String(Arrays.copyOfRange(buffer, 3, count)); + if (transportType == AdbTransportType.USB) { + Slog.d(TAG, "Received USB TLS disconnect message: " + key); + Message msg = mHandler.obtainMessage( + AdbDebuggingHandler.MESSAGE_ADB_DISCONNECT); + msg.obj = key; + mHandler.sendMessage(msg); + } else if (transportType == AdbTransportType.WIFI) { + Slog.d(TAG, "Received WIFI TLS disconnect key message: " + key); + Message msg = mHandler.obtainMessage( + AdbDebuggingHandler.MSG_WIFI_DEVICE_DISCONNECTED); + msg.obj = key; + mHandler.sendMessage(msg); + } else { + Slog.e(TAG, "Got unknown transport type from adbd (" + transportType + + ")"); + } } else { Slog.e(TAG, "Wrong message: " + (new String(Arrays.copyOfRange(buffer, 0, 2)))); @@ -268,7 +520,156 @@ public class AdbDebuggingManager { } } + class AdbConnectionInfo { + private String mBssid; + private String mSsid; + private int mPort; + + AdbConnectionInfo() { + mBssid = ""; + mSsid = ""; + mPort = -1; + } + + AdbConnectionInfo(String bssid, String ssid) { + mBssid = bssid; + mSsid = ssid; + } + + AdbConnectionInfo(AdbConnectionInfo other) { + mBssid = other.mBssid; + mSsid = other.mSsid; + mPort = other.mPort; + } + + public String getBSSID() { + return mBssid; + } + + public String getSSID() { + return mSsid; + } + + public int getPort() { + return mPort; + } + + public void setPort(int port) { + mPort = port; + } + + public void clear() { + mBssid = ""; + mSsid = ""; + mPort = -1; + } + } + + private void setAdbConnectionInfo(AdbConnectionInfo info) { + synchronized (mAdbConnectionInfo) { + if (info == null) { + mAdbConnectionInfo.clear(); + return; + } + mAdbConnectionInfo = info; + } + } + + private AdbConnectionInfo getAdbConnectionInfo() { + synchronized (mAdbConnectionInfo) { + return new AdbConnectionInfo(mAdbConnectionInfo); + } + } + class AdbDebuggingHandler extends Handler { + private NotificationManager mNotificationManager; + private boolean mAdbNotificationShown; + + private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + // We only care about when wifi is disabled, and when there is a wifi network + // change. + if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) { + int state = intent.getIntExtra( + WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_DISABLED); + if (state == WifiManager.WIFI_STATE_DISABLED) { + Slog.i(TAG, "Wifi disabled. Disabling adbwifi."); + Settings.Global.putInt(mContentResolver, + Settings.Global.ADB_WIFI_ENABLED, 0); + } + } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) { + // We only care about wifi type connections + NetworkInfo networkInfo = (NetworkInfo) intent.getParcelableExtra( + WifiManager.EXTRA_NETWORK_INFO); + if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) { + // Check for network disconnect + if (!networkInfo.isConnected()) { + Slog.i(TAG, "Network disconnected. Disabling adbwifi."); + Settings.Global.putInt(mContentResolver, + Settings.Global.ADB_WIFI_ENABLED, 0); + return; + } + + WifiManager wifiManager = + (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); + WifiInfo wifiInfo = wifiManager.getConnectionInfo(); + if (wifiInfo == null || wifiInfo.getNetworkId() == -1) { + Slog.i(TAG, "Not connected to any wireless network." + + " Not enabling adbwifi."); + Settings.Global.putInt(mContentResolver, + Settings.Global.ADB_WIFI_ENABLED, 0); + } + + // Check for network change + String bssid = wifiInfo.getBSSID(); + if (bssid == null || bssid.isEmpty()) { + Slog.e(TAG, "Unable to get the wifi ap's BSSID. Disabling adbwifi."); + Settings.Global.putInt(mContentResolver, + Settings.Global.ADB_WIFI_ENABLED, 0); + } + synchronized (mAdbConnectionInfo) { + if (!bssid.equals(mAdbConnectionInfo.getBSSID())) { + Slog.i(TAG, "Detected wifi network change. Disabling adbwifi."); + Settings.Global.putInt(mContentResolver, + Settings.Global.ADB_WIFI_ENABLED, 0); + } + } + } + } + } + }; + + private static final String ADB_NOTIFICATION_CHANNEL_ID_TV = "usbdevicemanager.adb.tv"; + + private boolean isTv() { + return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK); + } + + private void setupNotifications() { + if (mNotificationManager != null) { + return; + } + mNotificationManager = (NotificationManager) + mContext.getSystemService(Context.NOTIFICATION_SERVICE); + if (mNotificationManager == null) { + Slog.e(TAG, "Unable to setup notifications for wireless debugging"); + return; + } + + // Ensure that the notification channels are set up + if (isTv()) { + // TV-specific notification channel + mNotificationManager.createNotificationChannel( + new NotificationChannel(ADB_NOTIFICATION_CHANNEL_ID_TV, + mContext.getString( + com.android.internal.R.string + .adb_debugging_notification_channel_tv), + NotificationManager.IMPORTANCE_HIGH)); + } + } + // The default time to schedule the job to keep the keystore updated with a currently // connected key as well as to removed expired keys. static final long UPDATE_KEYSTORE_JOB_INTERVAL = 86400000; @@ -288,8 +689,50 @@ public class AdbDebuggingManager { static final int MESSAGE_ADB_UPDATE_KEYSTORE = 9; static final int MESSAGE_ADB_CONNECTED_KEY = 10; + // === Messages from the UI ============== + // UI asks adbd to enable adbdwifi + static final int MSG_ADBDWIFI_ENABLE = 11; + // UI asks adbd to disable adbdwifi + static final int MSG_ADBDWIFI_DISABLE = 12; + // Cancel pairing + static final int MSG_PAIRING_CANCEL = 14; + // Enable pairing by pairing code + static final int MSG_PAIR_PAIRING_CODE = 15; + // Enable pairing by QR code + static final int MSG_PAIR_QR_CODE = 16; + // UI asks to unpair (forget) a device. + static final int MSG_REQ_UNPAIR = 17; + // User allows debugging on the current network + static final int MSG_ADBWIFI_ALLOW = 18; + // User denies debugging on the current network + static final int MSG_ADBWIFI_DENY = 19; + + // === Messages from the PairingThread =========== + // Result of the pairing + static final int MSG_RESPONSE_PAIRING_RESULT = 20; + // The port opened for pairing + static final int MSG_RESPONSE_PAIRING_PORT = 21; + + // === Messages from adbd ================ + // Notifies us a wifi device connected. + static final int MSG_WIFI_DEVICE_CONNECTED = 22; + // Notifies us a wifi device disconnected. + static final int MSG_WIFI_DEVICE_DISCONNECTED = 23; + // Notifies us the TLS server is connected and listening + static final int MSG_SERVER_CONNECTED = 24; + // Notifies us the TLS server is disconnected + static final int MSG_SERVER_DISCONNECTED = 25; + + // === Messages we can send to adbd =========== + static final String MSG_DISCONNECT_DEVICE = "DD"; + static final String MSG_DISABLE_ADBDWIFI = "DA"; + private 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. + private int mAdbEnabledRefCount = 0; + private ContentObserver mAuthTimeObserver = new ContentObserver(this) { @Override public void onChange(boolean selfChange, Uri uri) { @@ -314,44 +757,111 @@ public class AdbDebuggingManager { mAdbKeyStore = adbKeyStore; } + // Show when at least one device is connected. + public void showAdbConnectedNotification(boolean show) { + final int id = SystemMessage.NOTE_ADB_WIFI_ACTIVE; + final int titleRes = com.android.internal.R.string.adbwifi_active_notification_title; + if (show == mAdbNotificationShown) { + return; + } + setupNotifications(); + if (!mAdbNotificationShown) { + Resources r = mContext.getResources(); + CharSequence title = r.getText(titleRes); + CharSequence message = r.getText( + com.android.internal.R.string.adbwifi_active_notification_message); + + Intent intent = new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_CLEAR_TASK); + PendingIntent pi = PendingIntent.getActivityAsUser(mContext, 0, + intent, 0, null, UserHandle.CURRENT); + + Notification notification = + new Notification.Builder(mContext, SystemNotificationChannels.DEVELOPER) + .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb) + .setWhen(0) + .setOngoing(true) + .setTicker(title) + .setDefaults(0) // please be quiet + .setColor(mContext.getColor( + com.android.internal.R.color + .system_notification_accent_color)) + .setContentTitle(title) + .setContentText(message) + .setContentIntent(pi) + .setVisibility(Notification.VISIBILITY_PUBLIC) + .extend(new Notification.TvExtender() + .setChannelId(ADB_NOTIFICATION_CHANNEL_ID_TV)) + .build(); + mAdbNotificationShown = true; + mNotificationManager.notifyAsUser(null, id, notification, + UserHandle.ALL); + } else { + mAdbNotificationShown = false; + mNotificationManager.cancelAsUser(null, id, UserHandle.ALL); + } + } + + private void startAdbDebuggingThread() { + ++mAdbEnabledRefCount; + if (DEBUG) Slog.i(TAG, "startAdbDebuggingThread ref=" + mAdbEnabledRefCount); + if (mAdbEnabledRefCount > 1) { + return; + } + + registerForAuthTimeChanges(); + mThread = new AdbDebuggingThread(); + mThread.start(); + + mAdbKeyStore.updateKeyStore(); + scheduleJobToUpdateAdbKeyStore(); + } + + private void stopAdbDebuggingThread() { + --mAdbEnabledRefCount; + if (DEBUG) Slog.i(TAG, "stopAdbDebuggingThread ref=" + mAdbEnabledRefCount); + if (mAdbEnabledRefCount > 0) { + return; + } + + if (mThread != null) { + mThread.stopListening(); + mThread = null; + } + + if (!mConnectedKeys.isEmpty()) { + for (Map.Entry<String, Integer> entry : mConnectedKeys.entrySet()) { + mAdbKeyStore.setLastConnectionTime(entry.getKey(), + System.currentTimeMillis()); + } + sendPersistKeyStoreMessage(); + mConnectedKeys.clear(); + mWifiConnectedKeys.clear(); + } + scheduleJobToUpdateAdbKeyStore(); + } + public void handleMessage(Message msg) { + if (mAdbKeyStore == null) { + mAdbKeyStore = new AdbKeyStore(); + } + switch (msg.what) { case MESSAGE_ADB_ENABLED: - if (mAdbEnabled) { + if (mAdbUsbEnabled) { break; } - registerForAuthTimeChanges(); - mAdbEnabled = true; - - mThread = new AdbDebuggingThread(); - mThread.start(); - - mAdbKeyStore = new AdbKeyStore(); - mAdbKeyStore.updateKeyStore(); - scheduleJobToUpdateAdbKeyStore(); + startAdbDebuggingThread(); + mAdbUsbEnabled = true; break; case MESSAGE_ADB_DISABLED: - if (!mAdbEnabled) { + if (!mAdbUsbEnabled) { break; } - - mAdbEnabled = false; - - if (mThread != null) { - mThread.stopListening(); - mThread = null; - } - - if (!mConnectedKeys.isEmpty()) { - for (String connectedKey : mConnectedKeys) { - mAdbKeyStore.setLastConnectionTime(connectedKey, - System.currentTimeMillis()); - } - sendPersistKeyStoreMessage(); - mConnectedKeys.clear(); - } - scheduleJobToUpdateAdbKeyStore(); + stopAdbDebuggingThread(); + mAdbUsbEnabled = false; break; case MESSAGE_ADB_ALLOW: { @@ -367,8 +877,8 @@ public class AdbDebuggingManager { if (mThread != null) { mThread.sendResponse("OK"); if (alwaysAllow) { - if (!mConnectedKeys.contains(key)) { - mConnectedKeys.add(key); + if (!mConnectedKeys.containsKey(key)) { + mConnectedKeys.put(key, 1); } mAdbKeyStore.setLastConnectionTime(key, System.currentTimeMillis()); sendPersistKeyStoreMessage(); @@ -407,7 +917,7 @@ public class AdbDebuggingManager { } logAdbConnectionChanged(key, AdbProtoEnums.AWAITING_USER_APPROVAL, false); mFingerprints = fingerprints; - startConfirmation(key, mFingerprints); + startConfirmationForKey(key, mFingerprints); break; } @@ -419,6 +929,7 @@ public class AdbDebuggingManager { if (mAdbKeyStore == null) { mAdbKeyStore = new AdbKeyStore(); } + mWifiConnectedKeys.clear(); mAdbKeyStore.deleteKeyStore(); cancelJobToUpdateAdbKeyStore(); break; @@ -428,12 +939,17 @@ public class AdbDebuggingManager { String key = (String) msg.obj; boolean alwaysAllow = false; if (key != null && key.length() > 0) { - if (mConnectedKeys.contains(key)) { + if (mConnectedKeys.containsKey(key)) { alwaysAllow = true; - mAdbKeyStore.setLastConnectionTime(key, System.currentTimeMillis()); - sendPersistKeyStoreMessage(); - scheduleJobToUpdateAdbKeyStore(); - mConnectedKeys.remove(key); + int refcount = mConnectedKeys.get(key) - 1; + if (refcount == 0) { + mAdbKeyStore.setLastConnectionTime(key, System.currentTimeMillis()); + sendPersistKeyStoreMessage(); + scheduleJobToUpdateAdbKeyStore(); + mConnectedKeys.remove(key); + } else { + mConnectedKeys.put(key, refcount); + } } } else { Slog.w(TAG, "Received a disconnected key message with an empty key"); @@ -451,8 +967,8 @@ public class AdbDebuggingManager { case MESSAGE_ADB_UPDATE_KEYSTORE: { if (!mConnectedKeys.isEmpty()) { - for (String connectedKey : mConnectedKeys) { - mAdbKeyStore.setLastConnectionTime(connectedKey, + for (Map.Entry<String, Integer> entry : mConnectedKeys.entrySet()) { + mAdbKeyStore.setLastConnectionTime(entry.getKey(), System.currentTimeMillis()); } sendPersistKeyStoreMessage(); @@ -469,8 +985,10 @@ public class AdbDebuggingManager { if (key == null || key.length() == 0) { Slog.w(TAG, "Received a connected key message with an empty key"); } else { - if (!mConnectedKeys.contains(key)) { - mConnectedKeys.add(key); + if (!mConnectedKeys.containsKey(key)) { + mConnectedKeys.put(key, 1); + } else { + mConnectedKeys.put(key, mConnectedKeys.get(key) + 1); } mAdbKeyStore.setLastConnectionTime(key, System.currentTimeMillis()); sendPersistKeyStoreMessage(); @@ -479,6 +997,199 @@ public class AdbDebuggingManager { } break; } + case MSG_ADBDWIFI_ENABLE: { + if (mAdbWifiEnabled) { + break; + } + + AdbConnectionInfo currentInfo = getCurrentWifiApInfo(); + if (currentInfo == null) { + Settings.Global.putInt(mContentResolver, + Settings.Global.ADB_WIFI_ENABLED, 0); + break; + } + + if (!verifyWifiNetwork(currentInfo.getBSSID(), + currentInfo.getSSID())) { + // This means that the network is not in the list of trusted networks. + // We'll give user a prompt on whether to allow wireless debugging on + // the current wifi network. + Settings.Global.putInt(mContentResolver, + Settings.Global.ADB_WIFI_ENABLED, 0); + break; + } + + setAdbConnectionInfo(currentInfo); + IntentFilter intentFilter = + new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION); + intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); + mContext.registerReceiver(mBroadcastReceiver, intentFilter); + + SystemProperties.set(WIFI_PERSISTENT_CONFIG_PROPERTY, "1"); + mConnectionPortPoller = + new AdbDebuggingManager.AdbConnectionPortPoller(mPortListener); + mConnectionPortPoller.start(); + + startAdbDebuggingThread(); + mAdbWifiEnabled = true; + + if (DEBUG) Slog.i(TAG, "adb start wireless adb"); + break; + } + case MSG_ADBDWIFI_DISABLE: + if (!mAdbWifiEnabled) { + break; + } + mAdbWifiEnabled = false; + setAdbConnectionInfo(null); + mContext.unregisterReceiver(mBroadcastReceiver); + + if (mThread != null) { + mThread.sendResponse(MSG_DISABLE_ADBDWIFI); + } + onAdbdWifiServerDisconnected(-1); + stopAdbDebuggingThread(); + break; + case MSG_ADBWIFI_ALLOW: + if (mAdbWifiEnabled) { + break; + } + String bssid = (String) msg.obj; + boolean alwaysAllow = msg.arg1 == 1; + if (alwaysAllow) { + mAdbKeyStore.addTrustedNetwork(bssid); + } + + // Let's check again to make sure we didn't switch networks while verifying + // the wifi bssid. + AdbConnectionInfo newInfo = getCurrentWifiApInfo(); + if (newInfo == null || !bssid.equals(newInfo.getBSSID())) { + break; + } + + setAdbConnectionInfo(newInfo); + Settings.Global.putInt(mContentResolver, + Settings.Global.ADB_WIFI_ENABLED, 1); + IntentFilter intentFilter = + new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION); + intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); + mContext.registerReceiver(mBroadcastReceiver, intentFilter); + + SystemProperties.set(WIFI_PERSISTENT_CONFIG_PROPERTY, "1"); + mConnectionPortPoller = + new AdbDebuggingManager.AdbConnectionPortPoller(mPortListener); + mConnectionPortPoller.start(); + + startAdbDebuggingThread(); + mAdbWifiEnabled = true; + + if (DEBUG) Slog.i(TAG, "adb start wireless adb"); + break; + case MSG_ADBWIFI_DENY: + Settings.Global.putInt(mContentResolver, + Settings.Global.ADB_WIFI_ENABLED, 0); + sendServerConnectionState(false, -1); + break; + case MSG_REQ_UNPAIR: { + String fingerprint = (String) msg.obj; + // Tell adbd to disconnect the device if connected. + String publicKey = mAdbKeyStore.findKeyFromFingerprint(fingerprint); + if (publicKey == null || publicKey.isEmpty()) { + Slog.e(TAG, "Not a known fingerprint [" + fingerprint + "]"); + break; + } + String cmdStr = MSG_DISCONNECT_DEVICE + publicKey; + if (mThread != null) { + mThread.sendResponse(cmdStr); + } + mAdbKeyStore.removeKey(publicKey); + // Send the updated paired devices list to the UI. + sendPairedDevicesToUI(mAdbKeyStore.getPairedDevices()); + break; + } + case MSG_RESPONSE_PAIRING_RESULT: { + Bundle bundle = (Bundle) msg.obj; + String publicKey = bundle.getString("publicKey"); + onPairingResult(publicKey); + // Send the updated paired devices list to the UI. + sendPairedDevicesToUI(mAdbKeyStore.getPairedDevices()); + break; + } + case MSG_RESPONSE_PAIRING_PORT: { + int port = (int) msg.obj; + sendPairingPortToUI(port); + break; + } + case MSG_PAIR_PAIRING_CODE: { + String pairingCode = createPairingCode(PAIRING_CODE_LENGTH); + updateUIPairCode(pairingCode); + mPairingThread = new PairingThread(pairingCode, null); + mPairingThread.start(); + break; + } + case MSG_PAIR_QR_CODE: { + Bundle bundle = (Bundle) msg.obj; + String serviceName = bundle.getString("serviceName"); + String password = bundle.getString("password"); + mPairingThread = new PairingThread(password, serviceName); + mPairingThread.start(); + break; + } + case MSG_PAIRING_CANCEL: + if (mPairingThread != null) { + mPairingThread.cancelPairing(); + try { + mPairingThread.join(); + } catch (InterruptedException e) { + Slog.w(TAG, "Error while waiting for pairing thread to quit."); + e.printStackTrace(); + } + mPairingThread = null; + } + break; + case MSG_WIFI_DEVICE_CONNECTED: { + String key = (String) msg.obj; + if (mWifiConnectedKeys.add(key)) { + sendPairedDevicesToUI(mAdbKeyStore.getPairedDevices()); + showAdbConnectedNotification(true); + } + break; + } + case MSG_WIFI_DEVICE_DISCONNECTED: { + String key = (String) msg.obj; + if (mWifiConnectedKeys.remove(key)) { + sendPairedDevicesToUI(mAdbKeyStore.getPairedDevices()); + if (mWifiConnectedKeys.isEmpty()) { + showAdbConnectedNotification(false); + } + } + break; + } + case MSG_SERVER_CONNECTED: { + int port = (int) msg.obj; + onAdbdWifiServerConnected(port); + synchronized (mAdbConnectionInfo) { + mAdbConnectionInfo.setPort(port); + } + Settings.Global.putInt(mContentResolver, + Settings.Global.ADB_WIFI_ENABLED, 1); + break; + } + case MSG_SERVER_DISCONNECTED: { + if (!mAdbWifiEnabled) { + break; + } + int port = (int) msg.obj; + onAdbdWifiServerDisconnected(port); + Settings.Global.putInt(mContentResolver, + Settings.Global.ADB_WIFI_ENABLED, 0); + stopAdbDebuggingThread(); + if (mConnectionPortPoller != null) { + mConnectionPortPoller.cancelAndWait(); + mConnectionPortPoller = null; + } + break; + } } } @@ -540,6 +1251,142 @@ public class AdbDebuggingManager { private void cancelJobToUpdateAdbKeyStore() { removeMessages(AdbDebuggingHandler.MESSAGE_ADB_UPDATE_KEYSTORE); } + + // Generates a random string of digits with size |size|. + private String createPairingCode(int size) { + String res = ""; + SecureRandom rand = new SecureRandom(); + for (int i = 0; i < size; ++i) { + res += rand.nextInt(10); + } + + return res; + } + + private void sendServerConnectionState(boolean connected, int port) { + Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION); + intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA, connected + ? AdbManager.WIRELESS_STATUS_CONNECTED + : AdbManager.WIRELESS_STATUS_DISCONNECTED); + intent.putExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, port); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + } + + private void onAdbdWifiServerConnected(int port) { + // Send the paired devices list to the UI + sendPairedDevicesToUI(mAdbKeyStore.getPairedDevices()); + sendServerConnectionState(true, port); + } + + private void onAdbdWifiServerDisconnected(int port) { + // The TLS server disconnected while we had wireless debugging enabled. + // Let's disable it. + mWifiConnectedKeys.clear(); + showAdbConnectedNotification(false); + sendServerConnectionState(false, port); + } + + /** + * Returns the [bssid, ssid] of the current access point. + */ + private AdbConnectionInfo getCurrentWifiApInfo() { + WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); + WifiInfo wifiInfo = wifiManager.getConnectionInfo(); + if (wifiInfo == null || wifiInfo.getNetworkId() == -1) { + Slog.i(TAG, "Not connected to any wireless network. Not enabling adbwifi."); + return null; + } + + String ssid = null; + if (wifiInfo.isPasspointAp() || wifiInfo.isOsuAp()) { + ssid = wifiInfo.getPasspointProviderFriendlyName(); + } else { + ssid = wifiInfo.getSSID(); + if (ssid == null || WifiManager.UNKNOWN_SSID.equals(ssid)) { + // OK, it's not in the connectionInfo; we have to go hunting for it + List<WifiConfiguration> networks = wifiManager.getConfiguredNetworks(); + int length = networks.size(); + for (int i = 0; i < length; i++) { + if (networks.get(i).networkId == wifiInfo.getNetworkId()) { + ssid = networks.get(i).SSID; + } + } + if (ssid == null) { + Slog.e(TAG, "Unable to get ssid of the wifi AP."); + return null; + } + } + } + + String bssid = wifiInfo.getBSSID(); + if (bssid == null || bssid.isEmpty()) { + Slog.e(TAG, "Unable to get the wifi ap's BSSID."); + return null; + } + return new AdbConnectionInfo(bssid, ssid); + } + + private boolean verifyWifiNetwork(String bssid, String ssid) { + // Check against a list of user-trusted networks. + if (mAdbKeyStore.isTrustedNetwork(bssid)) { + return true; + } + + // Ask user to confirm using wireless debugging on this network. + startConfirmationForNetwork(ssid, bssid); + return false; + } + + private void onPairingResult(String publicKey) { + if (publicKey == null) { + Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION); + intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA, AdbManager.WIRELESS_STATUS_FAIL); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + } else { + Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION); + intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA, + AdbManager.WIRELESS_STATUS_SUCCESS); + String fingerprints = getFingerprints(publicKey); + String hostname = "nouser@nohostname"; + String[] args = publicKey.split("\\s+"); + if (args.length > 1) { + hostname = args[1]; + } + PairDevice device = new PairDevice(fingerprints, hostname, false); + intent.putExtra(AdbManager.WIRELESS_PAIR_DEVICE_EXTRA, device); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + // Add the key into the keystore + mAdbKeyStore.setLastConnectionTime(publicKey, + System.currentTimeMillis()); + sendPersistKeyStoreMessage(); + scheduleJobToUpdateAdbKeyStore(); + } + } + + private void sendPairingPortToUI(int port) { + Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION); + intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA, + AdbManager.WIRELESS_STATUS_CONNECTED); + intent.putExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, port); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + } + + private void sendPairedDevicesToUI(Map<String, PairDevice> devices) { + Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRED_DEVICES_ACTION); + // Map is not serializable, so need to downcast + intent.putExtra(AdbManager.WIRELESS_DEVICES_EXTRA, (HashMap) devices); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + } + + private void updateUIPairCode(String code) { + if (DEBUG) Slog.i(TAG, "updateUIPairCode: " + code); + + Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION); + intent.putExtra(AdbManager.WIRELESS_PAIRING_CODE_EXTRA, code); + intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA, + AdbManager.WIRELESS_STATUS_PAIRING_CODE); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + } } private String getFingerprints(String key) { @@ -576,7 +1423,34 @@ public class AdbDebuggingManager { return sb.toString(); } - private void startConfirmation(String key, String fingerprints) { + private void startConfirmationForNetwork(String ssid, String bssid) { + List<Map.Entry<String, String>> extras = new ArrayList<Map.Entry<String, String>>(); + 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); + } + ComponentName componentName = ComponentName.unflattenFromString(componentString); + if (startConfirmationActivity(componentName, userInfo.getUserHandle(), extras) + || startConfirmationService(componentName, userInfo.getUserHandle(), + extras)) { + return; + } + Slog.e(TAG, "Unable to start customAdbWifiNetworkConfirmation[SecondaryUser]Component " + + componentString + " as an Activity or a Service"); + } + + private void startConfirmationForKey(String key, String fingerprints) { + List<Map.Entry<String, String>> extras = new ArrayList<Map.Entry<String, String>>(); + extras.add(new AbstractMap.SimpleEntry<String, String>("key", key)); + extras.add(new AbstractMap.SimpleEntry<String, String>("fingerprints", fingerprints)); int currentUserId = ActivityManager.getCurrentUser(); UserInfo userInfo = UserManager.get(mContext).getUserInfo(currentUserId); String componentString; @@ -591,9 +1465,9 @@ public class AdbDebuggingManager { R.string.config_customAdbPublicKeyConfirmationSecondaryUserComponent); } ComponentName componentName = ComponentName.unflattenFromString(componentString); - if (startConfirmationActivity(componentName, userInfo.getUserHandle(), key, fingerprints) + if (startConfirmationActivity(componentName, userInfo.getUserHandle(), extras) || startConfirmationService(componentName, userInfo.getUserHandle(), - key, fingerprints)) { + extras)) { return; } Slog.e(TAG, "unable to start customAdbPublicKeyConfirmation[SecondaryUser]Component " @@ -604,9 +1478,9 @@ public class AdbDebuggingManager { * @return true if the componentName led to an Activity that was started. */ private boolean startConfirmationActivity(ComponentName componentName, UserHandle userHandle, - String key, String fingerprints) { + List<Map.Entry<String, String>> extras) { PackageManager packageManager = mContext.getPackageManager(); - Intent intent = createConfirmationIntent(componentName, key, fingerprints); + Intent intent = createConfirmationIntent(componentName, extras); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); if (packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null) { try { @@ -623,8 +1497,8 @@ public class AdbDebuggingManager { * @return true if the componentName led to a Service that was started. */ private boolean startConfirmationService(ComponentName componentName, UserHandle userHandle, - String key, String fingerprints) { - Intent intent = createConfirmationIntent(componentName, key, fingerprints); + List<Map.Entry<String, String>> extras) { + Intent intent = createConfirmationIntent(componentName, extras); try { if (mContext.startServiceAsUser(intent, userHandle) != null) { return true; @@ -635,12 +1509,13 @@ public class AdbDebuggingManager { return false; } - private Intent createConfirmationIntent(ComponentName componentName, String key, - String fingerprints) { + private Intent createConfirmationIntent(ComponentName componentName, + List<Map.Entry<String, String>> extras) { Intent intent = new Intent(); intent.setClassName(componentName.getPackageName(), componentName.getClassName()); - intent.putExtra("key", key); - intent.putExtra("fingerprints", fingerprints); + for (Map.Entry<String, String> entry : extras) { + intent.putExtra(entry.getKey(), entry.getValue()); + } return intent; } @@ -733,7 +1608,8 @@ public class AdbDebuggingManager { mHandler.sendEmptyMessage(enabled ? AdbDebuggingHandler.MESSAGE_ADB_ENABLED : AdbDebuggingHandler.MESSAGE_ADB_DISABLED); } else if (transportType == AdbTransportType.WIFI) { - // TODO(joshuaduong): Not implemented + mHandler.sendEmptyMessage(enabled ? AdbDebuggingHandler.MSG_ADBDWIFI_ENABLE + : AdbDebuggingHandler.MSG_ADBDWIFI_DISABLE); } else { throw new IllegalArgumentException( "setAdbEnabled called with unimplemented transport type=" + transportType); @@ -767,6 +1643,87 @@ public class AdbDebuggingManager { } /** + * Allows wireless debugging on the network identified by {@code bssid} either once + * or always if {@code alwaysAllow} is {@code true}. + */ + public void allowWirelessDebugging(boolean alwaysAllow, String bssid) { + Message msg = mHandler.obtainMessage(AdbDebuggingHandler.MSG_ADBWIFI_ALLOW); + msg.arg1 = alwaysAllow ? 1 : 0; + msg.obj = bssid; + mHandler.sendMessage(msg); + } + + /** + * Denies wireless debugging connection on the last requested network. + */ + public void denyWirelessDebugging() { + mHandler.sendEmptyMessage(AdbDebuggingHandler.MSG_ADBWIFI_DENY); + } + + /** + * Returns the port adbwifi is currently opened on. + */ + public int getAdbWirelessPort() { + AdbConnectionInfo info = getAdbConnectionInfo(); + if (info == null) { + return 0; + } + return info.getPort(); + } + + /** + * Returns the list of paired devices. + */ + public Map<String, PairDevice> getPairedDevices() { + AdbKeyStore keystore = new AdbKeyStore(); + return keystore.getPairedDevices(); + } + + /** + * Unpair with device + */ + public void unpairDevice(String fingerprint) { + Message message = Message.obtain(mHandler, + AdbDebuggingHandler.MSG_REQ_UNPAIR, + fingerprint); + mHandler.sendMessage(message); + } + + /** + * Enable pairing by pairing code + */ + public void enablePairingByPairingCode() { + mHandler.sendEmptyMessage(AdbDebuggingHandler.MSG_PAIR_PAIRING_CODE); + } + + /** + * Enable pairing by pairing code + */ + public void enablePairingByQrCode(String serviceName, String password) { + Bundle bundle = new Bundle(); + bundle.putString("serviceName", serviceName); + bundle.putString("password", password); + Message message = Message.obtain(mHandler, + AdbDebuggingHandler.MSG_PAIR_QR_CODE, + bundle); + mHandler.sendMessage(message); + } + + /** + * Disables pairing + */ + public void disablePairing() { + mHandler.sendEmptyMessage(AdbDebuggingHandler.MSG_PAIRING_CANCEL); + } + + /** + * Status enabled/disabled check + */ + public boolean isAdbWifiEnabled() { + return mAdbWifiEnabled; + } + + /** * Sends a message to the handler to persist the keystore. */ private void sendPersistKeyStoreMessage() { @@ -819,9 +1776,19 @@ public class AdbDebuggingManager { private File mKeyFile; private AtomicFile mAtomicKeyFile; + private List<String> mTrustedNetworks; + 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"; + private static final String XML_ATTRIBUTE_VERSION = "version"; private static final String XML_TAG_ADB_KEY = "adbKey"; private static final String XML_ATTRIBUTE_KEY = "key"; private static final String XML_ATTRIBUTE_LAST_CONNECTION = "lastConnection"; + // A list of trusted networks a device can always wirelessly debug on (always allow). + // TODO: Move trusted networks list into a different file? + private static final String XML_TAG_WIFI_ACCESS_POINT = "wifiAP"; + private static final String XML_ATTRIBUTE_WIFI_BSSID = "bssid"; + private static final String SYSTEM_KEY_FILE = "/adb_keys"; /** @@ -848,10 +1815,48 @@ public class AdbDebuggingManager { private void init() { initKeyFile(); mKeyMap = getKeyMap(); + mTrustedNetworks = getTrustedNetworks(); mSystemKeys = getSystemKeysFromFile(SYSTEM_KEY_FILE); addUserKeysToKeyStore(); } + public void addTrustedNetwork(String bssid) { + mTrustedNetworks.add(bssid); + sendPersistKeyStoreMessage(); + } + + public Map<String, PairDevice> getPairedDevices() { + Map<String, PairDevice> pairedDevices = new HashMap<String, PairDevice>(); + for (Map.Entry<String, Long> keyEntry : mKeyMap.entrySet()) { + String fingerprints = getFingerprints(keyEntry.getKey()); + String hostname = "nouser@nohostname"; + String[] args = keyEntry.getKey().split("\\s+"); + if (args.length > 1) { + hostname = args[1]; + } + pairedDevices.put(keyEntry.getKey(), new PairDevice( + hostname, fingerprints, mWifiConnectedKeys.contains(keyEntry.getKey()))); + } + return pairedDevices; + } + + public String findKeyFromFingerprint(String fingerprint) { + for (Map.Entry<String, Long> entry : mKeyMap.entrySet()) { + String f = getFingerprints(entry.getKey()); + if (fingerprint.equals(f)) { + return entry.getKey(); + } + } + return null; + } + + public void removeKey(String key) { + if (mKeyMap.containsKey(key)) { + mKeyMap.remove(key); + sendPersistKeyStoreMessage(); + } + } + /** * Initializes the key file that will be used to persist the adb grants. */ @@ -921,6 +1926,78 @@ public class AdbDebuggingManager { try (FileInputStream keyStream = mAtomicKeyFile.openRead()) { XmlPullParser parser = Xml.newPullParser(); parser.setInput(keyStream, StandardCharsets.UTF_8.name()); + // 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; + } + int keystoreVersion = Integer.parseInt( + parser.getAttributeValue(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; + } + } + 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 = Long.valueOf( + parser.getAttributeValue(null, XML_ATTRIBUTE_LAST_CONNECTION)); + } catch (NumberFormatException e) { + Slog.e(TAG, + "Caught a NumberFormatException parsing the last connection time: " + + e); + XmlUtils.skipCurrentTag(parser); + continue; + } + keyMap.put(key, connectionTime); + } + } 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(); + } + 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; + } + } + if (!mAtomicKeyFile.exists()) { + return keyMap; + } + try (FileInputStream keyStream = mAtomicKeyFile.openRead()) { + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(keyStream, StandardCharsets.UTF_8.name()); XmlUtils.beginDocument(parser, XML_TAG_ADB_KEY); while (parser.next() != XmlPullParser.END_DOCUMENT) { String tagName = parser.getName(); @@ -951,6 +2028,63 @@ public class AdbDebuggingManager { } /** + * 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; + } + } + if (!mAtomicKeyFile.exists()) { + return trustedNetworks; + } + try (FileInputStream keyStream = mAtomicKeyFile.openRead()) { + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(keyStream, StandardCharsets.UTF_8.name()); + // 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 = Integer.parseInt( + parser.getAttributeValue(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; + } + + /** * Updates the keystore with keys that were previously set to be always allowed before the * connection time of keys was tracked. */ @@ -986,7 +2120,7 @@ public class AdbDebuggingManager { // if there is nothing in the key map then ensure any keys left in the keystore files // are deleted as well. filterOutOldKeys(); - if (mKeyMap.isEmpty()) { + if (mKeyMap.isEmpty() && mTrustedNetworks.isEmpty()) { deleteKeyStore(); return; } @@ -1004,6 +2138,8 @@ public class AdbDebuggingManager { serializer.setOutput(keyStream, StandardCharsets.UTF_8.name()); serializer.startDocument(null, true); + serializer.startTag(null, XML_KEYSTORE_START_TAG); + serializer.attribute(null, XML_ATTRIBUTE_VERSION, String.valueOf(KEYSTORE_VERSION)); for (Map.Entry<String, Long> keyEntry : mKeyMap.entrySet()) { serializer.startTag(null, XML_TAG_ADB_KEY); serializer.attribute(null, XML_ATTRIBUTE_KEY, keyEntry.getKey()); @@ -1011,7 +2147,12 @@ public class AdbDebuggingManager { String.valueOf(keyEntry.getValue())); serializer.endTag(null, XML_TAG_ADB_KEY); } - + for (String bssid : mTrustedNetworks) { + serializer.startTag(null, XML_TAG_WIFI_ACCESS_POINT); + serializer.attribute(null, XML_ATTRIBUTE_WIFI_BSSID, bssid); + serializer.endTag(null, XML_TAG_WIFI_ACCESS_POINT); + } + serializer.endTag(null, XML_KEYSTORE_START_TAG); serializer.endDocument(); mAtomicKeyFile.finishWrite(keyStream); } catch (IOException e) { @@ -1072,6 +2213,7 @@ public class AdbDebuggingManager { */ public void deleteKeyStore() { mKeyMap.clear(); + mTrustedNetworks.clear(); deleteKeyFile(); if (mAtomicKeyFile == null) { return; @@ -1153,5 +2295,14 @@ public class AdbDebuggingManager { return false; } } + + /** + * Returns whether the specified bssid is in the list of trusted networks. This requires + * that the user previously allowed wireless debugging on this network and selected the + * option to 'Always allow'. + */ + public boolean isTrustedNetwork(String bssid) { + return mTrustedNetworks.contains(bssid); + } } } diff --git a/services/core/java/com/android/server/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java index 0d161b943d15..7ccb28474604 100644 --- a/services/core/java/com/android/server/adb/AdbService.java +++ b/services/core/java/com/android/server/adb/AdbService.java @@ -21,8 +21,10 @@ import android.annotation.NonNull; import android.annotation.UserIdInt; import android.content.ContentResolver; import android.content.Context; +import android.content.Intent; import android.content.pm.PackageManager; import android.database.ContentObserver; +import android.debug.AdbManager; import android.debug.AdbManagerInternal; import android.debug.AdbTransportType; import android.debug.IAdbManager; @@ -34,6 +36,7 @@ import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.os.SystemProperties; +import android.os.UserHandle; import android.provider.Settings; import android.service.adb.AdbServiceDumpProto; import android.sysprop.AdbProperties; @@ -44,6 +47,7 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.util.DumpUtils; import com.android.internal.util.IndentingPrintWriter; +import com.android.internal.util.Preconditions; import com.android.internal.util.dump.DualDumpOutputStream; import com.android.server.FgThread; import com.android.server.LocalServices; @@ -54,6 +58,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.Collections; import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; /** * The Android Debug Bridge (ADB) service. This controls the availability of ADB and authorization @@ -61,6 +66,27 @@ import java.util.Map; */ public class AdbService extends IAdbManager.Stub { /** + * Adb native daemon. + */ + static final String ADBD = "adbd"; + + /** + * Command to start native service. + */ + static final String CTL_START = "ctl.start"; + + /** + * Command to start native service. + */ + static final String CTL_STOP = "ctl.stop"; + + // The tcp port adb is currently using + AtomicInteger mConnectionPort = new AtomicInteger(-1); + + private final AdbConnectionPortListener mPortListener = new AdbConnectionPortListener(); + private AdbDebuggingManager.AdbConnectionPortPoller mConnectionPortPoller; + + /** * Manages the service lifecycle for {@code AdbService} in {@code SystemServer}. */ public static class Lifecycle extends SystemService { @@ -129,9 +155,8 @@ public class AdbService extends IAdbManager.Stub { mIsAdbUsbEnabled = containsFunction( SystemProperties.get(USB_PERSISTENT_CONFIG_PROPERTY, ""), UsbManager.USB_FUNCTION_ADB); - // TODO(joshuaduong): Read the adb wifi state from a persistent system - // property (persist.sys.adb.wifi). - mIsAdbWifiEnabled = false; + mIsAdbWifiEnabled = "1".equals( + SystemProperties.get(WIFI_PERSISTENT_CONFIG_PROPERTY, "0")); // register observer to listen for settings changes mObserver = new AdbSettingsObserver(); @@ -189,6 +214,7 @@ public class AdbService extends IAdbManager.Stub { * May also contain vendor-specific default functions for testing purposes. */ private static final String USB_PERSISTENT_CONFIG_PROPERTY = "persist.sys.usb.config"; + private static final String WIFI_PERSISTENT_CONFIG_PROPERTY = "persist.adb.tls_server.enable"; private final Context mContext; private final ContentResolver mContentResolver; @@ -245,8 +271,9 @@ public class AdbService extends IAdbManager.Stub { } @Override - public void allowDebugging(boolean alwaysAllow, String publicKey) { + public void allowDebugging(boolean alwaysAllow, @NonNull String publicKey) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null); + Preconditions.checkStringNotEmpty(publicKey); if (mDebuggingManager != null) { mDebuggingManager.allowDebugging(alwaysAllow, publicKey); } @@ -296,53 +323,118 @@ public class AdbService extends IAdbManager.Stub { } @Override - public void allowWirelessDebugging(boolean alwaysAllow, String bssid) { + public void allowWirelessDebugging(boolean alwaysAllow, @NonNull String bssid) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null); - // TODO(joshuaduong): NOT IMPLEMENTED + Preconditions.checkStringNotEmpty(bssid); + if (mDebuggingManager != null) { + mDebuggingManager.allowWirelessDebugging(alwaysAllow, bssid); + } } @Override public void denyWirelessDebugging() { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null); - // TODO(joshuaduong): NOT IMPLEMENTED + if (mDebuggingManager != null) { + mDebuggingManager.denyWirelessDebugging(); + } } @Override public Map<String, PairDevice> getPairedDevices() { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null); - // TODO(joshuaduong): NOT IMPLEMENTED + if (mDebuggingManager != null) { + return mDebuggingManager.getPairedDevices(); + } return null; } @Override - public void unpairDevice(String fingerprint) { + public void unpairDevice(@NonNull String fingerprint) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null); - // TODO(joshuaduong): NOT IMPLEMENTED + Preconditions.checkStringNotEmpty(fingerprint); + if (mDebuggingManager != null) { + mDebuggingManager.unpairDevice(fingerprint); + } } @Override public void enablePairingByPairingCode() { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null); - // TODO(joshuaduong): NOT IMPLEMENTED + if (mDebuggingManager != null) { + mDebuggingManager.enablePairingByPairingCode(); + } } @Override - public void enablePairingByQrCode(String serviceName, String password) { + public void enablePairingByQrCode(@NonNull String serviceName, @NonNull String password) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null); - // TODO(joshuaduong): NOT IMPLEMENTED + Preconditions.checkStringNotEmpty(serviceName); + Preconditions.checkStringNotEmpty(password); + if (mDebuggingManager != null) { + mDebuggingManager.enablePairingByQrCode(serviceName, password); + } } @Override public void disablePairing() { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null); - // TODO(joshuaduong): NOT IMPLEMENTED + if (mDebuggingManager != null) { + mDebuggingManager.disablePairing(); + } } @Override public int getAdbWirelessPort() { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null); - // TODO(joshuaduong): NOT IMPLEMENTED - return 0; + if (mDebuggingManager != null) { + return mDebuggingManager.getAdbWirelessPort(); + } + // If ro.adb.secure=0 + return mConnectionPort.get(); + } + + /** + * This listener is only used when ro.adb.secure=0. Otherwise, AdbDebuggingManager will + * do this. + */ + class AdbConnectionPortListener implements AdbDebuggingManager.AdbConnectionPortListener { + public void onPortReceived(int port) { + if (port > 0 && port <= 65535) { + mConnectionPort.set(port); + } else { + mConnectionPort.set(-1); + // Turn off wifi debugging, since the server did not start. + try { + Settings.Global.putInt(mContentResolver, + Settings.Global.ADB_WIFI_ENABLED, 0); + } catch (SecurityException e) { + // If UserManager.DISALLOW_DEBUGGING_FEATURES is on, that this setting can't + // be changed. + Slog.d(TAG, "ADB_ENABLED is restricted."); + } + } + broadcastPortInfo(mConnectionPort.get()); + } + } + + private void broadcastPortInfo(int port) { + Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION); + intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA, (port >= 0) + ? AdbManager.WIRELESS_STATUS_CONNECTED + : AdbManager.WIRELESS_STATUS_DISCONNECTED); + intent.putExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, port); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + Slog.i(TAG, "sent port broadcast port=" + port); + } + + private void startAdbd() { + SystemProperties.set(CTL_START, ADBD); + } + + private void stopAdbd() { + if (!mIsAdbUsbEnabled && !mIsAdbWifiEnabled) { + SystemProperties.set(CTL_STOP, ADBD); + } } private void setAdbEnabled(boolean enable, byte transportType) { @@ -356,11 +448,33 @@ public class AdbService extends IAdbManager.Stub { mIsAdbUsbEnabled = enable; } else if (transportType == AdbTransportType.WIFI && enable != mIsAdbWifiEnabled) { mIsAdbWifiEnabled = enable; + if (mIsAdbWifiEnabled) { + if (!AdbProperties.secure().orElse(false) && mDebuggingManager == null) { + // Start adbd. If this is secure adb, then we defer enabling adb over WiFi. + SystemProperties.set(WIFI_PERSISTENT_CONFIG_PROPERTY, "1"); + mConnectionPortPoller = + new AdbDebuggingManager.AdbConnectionPortPoller(mPortListener); + mConnectionPortPoller.start(); + } + } else { + // Stop adb over WiFi. + SystemProperties.set(WIFI_PERSISTENT_CONFIG_PROPERTY, "0"); + if (mConnectionPortPoller != null) { + mConnectionPortPoller.cancelAndWait(); + mConnectionPortPoller = null; + } + } } else { // No change return; } + if (enable) { + startAdbd(); + } else { + stopAdbd(); + } + for (IAdbTransport transport : mTransports.values()) { try { transport.onAdbEnabled(enable, transportType); diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index e888f2a4bae5..27bd58ec3010 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -24,6 +24,7 @@ cc_library_static { "BroadcastRadio/regions.cpp", "stats/PowerStatsPuller.cpp", "stats/SubsystemSleepStatePuller.cpp", + "com_android_server_adb_AdbDebuggingManager.cpp", "com_android_server_am_BatteryStatsService.cpp", "com_android_server_connectivity_Vpn.cpp", "com_android_server_ConsumerIrService.cpp", @@ -86,6 +87,8 @@ cc_library_static { cc_defaults { name: "libservices.core-libs", shared_libs: [ + "libadb_pairing_server", + "libadb_pairing_connection", "libandroid_runtime", "libandroidfw", "libaudioclient", diff --git a/services/core/jni/com_android_server_adb_AdbDebuggingManager.cpp b/services/core/jni/com_android_server_adb_AdbDebuggingManager.cpp new file mode 100644 index 000000000000..9c834aaece85 --- /dev/null +++ b/services/core/jni/com_android_server_adb_AdbDebuggingManager.cpp @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2019 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. + */ + +#define LOG_TAG "AdbDebuggingManager-JNI" + +#define LOG_NDEBUG 0 + +#include <algorithm> +#include <condition_variable> +#include <mutex> +#include <optional> +#include <random> +#include <string> +#include <vector> + +#include <adb/pairing/pairing_server.h> +#include <android-base/properties.h> +#include <utils/Log.h> + +#include <nativehelper/JNIHelp.h> +#include "jni.h" + +namespace android { + +// ---------------------------------------------------------------------------- +namespace { + +template <class T, class N> +class JSmartWrapper { +public: + JSmartWrapper(JNIEnv* env, T* jData) : mEnv(env), mJData(jData) {} + + virtual ~JSmartWrapper() = default; + + const N* data() const { return mRawData; } + + jsize size() const { return mSize; } + +protected: + N* mRawData = nullptr; + JNIEnv* mEnv = nullptr; + T* mJData = nullptr; + jsize mSize = 0; +}; // JSmartWrapper + +class JStringUTFWrapper : public JSmartWrapper<jstring, const char> { +public: + explicit JStringUTFWrapper(JNIEnv* env, jstring* str) : JSmartWrapper(env, str) { + mRawData = env->GetStringUTFChars(*str, NULL); + mSize = env->GetStringUTFLength(*str); + } + + virtual ~JStringUTFWrapper() { + if (data()) { + mEnv->ReleaseStringUTFChars(*mJData, mRawData); + } + } +}; // JStringUTFWrapper + +struct ServerDeleter { + void operator()(PairingServerCtx* p) { pairing_server_destroy(p); } +}; +using PairingServerPtr = std::unique_ptr<PairingServerCtx, ServerDeleter>; +struct PairingResultWaiter { + std::mutex mutex_; + std::condition_variable cv_; + std::optional<bool> is_valid_; + PeerInfo peer_info_; + + static void ResultCallback(const PeerInfo* peer_info, void* opaque) { + auto* p = reinterpret_cast<PairingResultWaiter*>(opaque); + { + std::unique_lock<std::mutex> lock(p->mutex_); + if (peer_info) { + memcpy(&(p->peer_info_), peer_info, sizeof(PeerInfo)); + } + p->is_valid_ = (peer_info != nullptr); + } + p->cv_.notify_one(); + } +}; + +PairingServerPtr sServer; +std::unique_ptr<PairingResultWaiter> sWaiter; +} // namespace + +static jint native_pairing_start(JNIEnv* env, jobject thiz, jstring guid, jstring password) { + // Server-side only sends its GUID on success. + PeerInfo system_info = {}; + system_info.type = ADB_DEVICE_GUID; + JStringUTFWrapper guidWrapper(env, &guid); + memcpy(system_info.data, guidWrapper.data(), guidWrapper.size()); + + JStringUTFWrapper passwordWrapper(env, &password); + + // Create the pairing server + sServer = PairingServerPtr( + pairing_server_new_no_cert(reinterpret_cast<const uint8_t*>(passwordWrapper.data()), + passwordWrapper.size(), &system_info, 0)); + + sWaiter.reset(new PairingResultWaiter); + uint16_t port = pairing_server_start(sServer.get(), sWaiter->ResultCallback, sWaiter.get()); + if (port == 0) { + ALOGE("Failed to start pairing server"); + return -1; + } + + return port; +} + +static void native_pairing_cancel(JNIEnv* /* env */, jclass /* clazz */) { + if (sServer != nullptr) { + sServer.reset(); + } +} + +static jboolean native_pairing_wait(JNIEnv* env, jobject thiz) { + ALOGI("Waiting for pairing server to complete"); + std::unique_lock<std::mutex> lock(sWaiter->mutex_); + if (!sWaiter->is_valid_.has_value()) { + sWaiter->cv_.wait(lock, [&]() { return sWaiter->is_valid_.has_value(); }); + } + if (!*(sWaiter->is_valid_)) { + return JNI_FALSE; + } + + std::string peer_public_key = reinterpret_cast<char*>(sWaiter->peer_info_.data); + // Write to PairingThread's member variables + jclass clazz = env->GetObjectClass(thiz); + jfieldID mPublicKey = env->GetFieldID(clazz, "mPublicKey", "Ljava/lang/String;"); + jstring jpublickey = env->NewStringUTF(peer_public_key.c_str()); + env->SetObjectField(thiz, mPublicKey, jpublickey); + return JNI_TRUE; +} + +// ---------------------------------------------------------------------------- + +static const JNINativeMethod gPairingThreadMethods[] = { + /* name, signature, funcPtr */ + {"native_pairing_start", "(Ljava/lang/String;Ljava/lang/String;)I", + (void*)native_pairing_start}, + {"native_pairing_cancel", "()V", (void*)native_pairing_cancel}, + {"native_pairing_wait", "()Z", (void*)native_pairing_wait}, +}; + +int register_android_server_AdbDebuggingManager(JNIEnv* env) { + int res = jniRegisterNativeMethods(env, + "com/android/server/adb/AdbDebuggingManager$PairingThread", + gPairingThreadMethods, NELEM(gPairingThreadMethods)); + (void)res; // Faked use when LOG_NDEBUG. + LOG_FATAL_IF(res < 0, "Unable to register native methods."); + return 0; +} + +} /* namespace android */ diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index f9238e3977c6..a5339a5d2e62 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -61,6 +61,7 @@ int register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl int register_android_server_incremental_IncrementalManagerService(JNIEnv* env); int register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(JNIEnv* env); int register_android_server_stats_pull_StatsPullAtomService(JNIEnv* env); +int register_android_server_AdbDebuggingManager(JNIEnv* env); }; using namespace android; @@ -115,5 +116,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) register_android_server_incremental_IncrementalManagerService(env); register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(env); register_android_server_stats_pull_StatsPullAtomService(env); + register_android_server_AdbDebuggingManager(env); return JNI_VERSION_1_4; } diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index 84f411f33157..07cc2d49d9e5 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -1720,21 +1720,6 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser private static final int ENUMERATION_TIME_OUT_MS = 2000; /** - * Command to start native service. - */ - protected static final String CTL_START = "ctl.start"; - - /** - * Command to start native service. - */ - protected static final String CTL_STOP = "ctl.stop"; - - /** - * Adb native daemon. - */ - protected static final String ADBD = "adbd"; - - /** * Gadget HAL fully qualified instance name for registering for ServiceNotification. */ protected static final String GADGET_HAL_FQ_NAME = @@ -1932,17 +1917,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser return; } try { - if ((config & UsbManager.FUNCTION_ADB) != 0) { - /** - * Start adbd if ADB function is included in the configuration. - */ - setSystemProperty(CTL_START, ADBD); - } else { - /** - * Stop adbd otherwise. - */ - setSystemProperty(CTL_STOP, ADBD); - } + // Adbd will be started by AdbService once Global.ADB_ENABLED is set. UsbGadgetCallback usbGadgetCallback = new UsbGadgetCallback(mCurrentRequest, config, chargingFunctions); mGadgetProxy.setCurrentUsbFunctions(config, usbGadgetCallback, |