Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1e08a64a authored by Fabien Sanglard's avatar Fabien Sanglard Committed by Android (Google) Code Review
Browse files

Merge changes Ie8873e60,I39fa1e6c,I5fc79feb into main

* changes:
  Extract PairingThread out of AdbDebuggingManager
  Simplify message between ADB Manager/PairThread
  Refactor and simplify native_pairing_wait
parents a20b444e 55ffa01e
Loading
Loading
Loading
Loading
+13 −131
Original line number Diff line number Diff line
@@ -42,15 +42,12 @@ import android.debug.AdbManager;
import android.debug.AdbNotifications;
import android.debug.AdbProtoEnums;
import android.debug.AdbTransportType;
import android.debug.IAdbTransport;
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;
@@ -61,7 +58,6 @@ import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.SystemService;
import android.os.UserHandle;
import android.os.UserManager;
@@ -74,7 +70,6 @@ import android.util.Slog;
import android.util.Xml;

import com.android.internal.R;
import com.android.internal.annotations.Keep;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.util.FrameworkStatsLog;
@@ -155,14 +150,14 @@ public class AdbDebuggingManager {
    @Nullable private final File mUserKeyFile;
    @Nullable private final File mTempKeysFile;

    private static final String WIFI_PERSISTENT_GUID =
            "persist.adb.wifi.guid";
    static final String WIFI_PERSISTENT_GUID = "persist.adb.wifi.guid";
    private static final int PAIRING_CODE_LENGTH = 6;
    /**
     * The maximum time to wait for the adbd service to change state when toggling.
     */
    private static final long ADBD_STATE_CHANGE_TIMEOUT = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
    private PairingThread mPairingThread = null;

    private AdbPairingThread mAdbPairingThread = null;
    // A list of keys connected via wifi
    private final Set<String> mWifiConnectedKeys = new HashSet<>();
    // The current info of the adbwifi connection.
@@ -232,119 +227,7 @@ public class AdbDebuggingManager {
        mConnectionPortPoller = null;
    }

    class PairingThread extends Thread implements NsdManager.RegistrationListener {
        private NsdManager mNsdManager;
        @Keep private String mPublicKey;
        private String mPairingCode;
        private String mGuid;
        private String mServiceName;
        // From RFC6763 (https://tools.ietf.org/html/rfc6763#section-7.2),
        // The rules for Service Names [RFC6335] state that they may be no more
        // than fifteen characters long (not counting the mandatory underscore),
        // 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";
        private final String mServiceType = String.format("_%s._tcp.", SERVICE_PROTOCOL);
        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() {
            // 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 (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);
        }

        @Override
        public void start() {
            /*
             * If a user is fast enough to click cancel, native_pairing_cancel can be invoked
             * while native_pairing_start is running which run the destruction of the object
             * while it is being constructed. Here we start the pairing server on foreground
             * Thread so native_pairing_cancel can never be called concurrently. Then we let
             * the pairing server run on a background Thread.
             */
            if (mGuid.isEmpty()) {
                Slog.e(TAG, "adbwifi guid was not set");
                return;
            }
            mPort = native_pairing_start(mGuid, mPairingCode);
            if (mPort <= 0) {
                Slog.e(TAG, "Unable to start pairing server");
                return;
            }

            super.start();
        }

        public void cancelPairing() {
            native_pairing_cancel();
        }

        @Override
        public void onServiceRegistered(NsdServiceInfo serviceInfo) {
            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) {
            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);
        }
    }

    @VisibleForTesting
    static class AdbDebuggingThread extends Thread {
@@ -1168,8 +1051,7 @@ public class AdbDebuggingManager {
                    break;
                }
                case MSG_RESPONSE_PAIRING_RESULT: {
                    Bundle bundle = (Bundle) msg.obj;
                    String publicKey = bundle.getString("publicKey");
                        String publicKey = (String) msg.obj;
                    onPairingResult(publicKey);
                    // Send the updated paired devices list to the UI.
                    sendPairedDevicesToUI(mAdbKeyStore.getPairedDevices());
@@ -1183,28 +1065,29 @@ public class AdbDebuggingManager {
                case MSG_PAIR_PAIRING_CODE: {
                    String pairingCode = createPairingCode(PAIRING_CODE_LENGTH);
                    updateUIPairCode(pairingCode);
                    mPairingThread = new PairingThread(pairingCode, null);
                    mPairingThread.start();
                        mAdbPairingThread = new AdbPairingThread(pairingCode, null, mContext, this);
                        mAdbPairingThread.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();
                        mAdbPairingThread =
                                new AdbPairingThread(password, serviceName, mContext, this);
                        mAdbPairingThread.start();
                    break;
                }
                case MSG_PAIRING_CANCEL:
                    if (mPairingThread != null) {
                        mPairingThread.cancelPairing();
                    if (mAdbPairingThread != null) {
                        mAdbPairingThread.cancelPairing();
                        try {
                            mPairingThread.join();
                            mAdbPairingThread.join();
                        } catch (InterruptedException e) {
                            Slog.w(TAG, "Error while waiting for pairing thread to quit.");
                            e.printStackTrace();
                        }
                        mPairingThread = null;
                        mAdbPairingThread = null;
                    }
                    break;
                case MSG_WIFI_DEVICE_CONNECTED: {
@@ -1283,7 +1166,6 @@ public class AdbDebuggingManager {
                    authWindow, state, alwaysAllow);
        }


        /**
         * Schedules a job to update the connection time of the currently connected key and filter
         * out any keys that are beyond their expiration time.
+145 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.adb;

import android.content.Context;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
import android.os.Handler;
import android.os.Message;
import android.os.SystemProperties;
import android.util.Slog;

import com.android.internal.annotations.VisibleForTesting;
import com.android.server.adb.AdbDebuggingManager.AdbDebuggingHandler;

class AdbPairingThread extends Thread implements NsdManager.RegistrationListener {
    private static final String TAG = AdbPairingThread.class.getSimpleName();
    private final NsdManager mNsdManager;
    private final Context mContext;
    private final String mPairingCode;
    private final String mGuid;
    private String mServiceName;

    // From RFC6763 (https://tools.ietf.org/html/rfc6763#section-7.2),
    // The rules for Service Names [RFC6335] state that they may be no more
    // than fifteen characters long (not counting the mandatory underscore),
    // 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";
    private final String mServiceType = String.format("_%s._tcp.", SERVICE_PROTOCOL);
    private int mPort;

    private final Handler mHandler;

    AdbPairingThread(String pairingCode, String serviceName, Context context, Handler handler) {
        super(TAG);
        mPairingCode = pairingCode;
        mGuid = SystemProperties.get(AdbDebuggingManager.WIFI_PERSISTENT_GUID);
        mServiceName = serviceName;
        if (serviceName == null || serviceName.isEmpty()) {
            mServiceName = mGuid;
        }
        mPort = -1;
        mContext = context;
        mNsdManager = (NsdManager) mContext.getSystemService(Context.NSD_SERVICE);
        mHandler = handler;
    }

    @Override
    public void run() {
        // 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);

        String publicKey = native_pairing_wait();
        if (publicKey != null) {
            Slog.i(TAG, "Pairing succeeded key=" + publicKey);
        } else {
            Slog.i(TAG, "Pairing failed");
        }

        mNsdManager.unregisterService(this);

        Message message =
                Message.obtain(
                        mHandler, AdbDebuggingHandler.MSG_RESPONSE_PAIRING_RESULT, publicKey);
        mHandler.sendMessage(message);
    }

    @Override
    public void start() {
        /*
         * If a user is fast enough to click cancel, native_pairing_cancel can be invoked
         * while native_pairing_start is running which run the destruction of the object
         * while it is being constructed. Here we start the pairing server on foreground
         * Thread so native_pairing_cancel can never be called concurrently. Then we let
         * the pairing server run on a background Thread.
         */
        if (mGuid.isEmpty()) {
            Slog.e(TAG, "adbwifi guid was not set");
            return;
        }
        mPort = native_pairing_start(mGuid, mPairingCode);
        if (mPort <= 0) {
            Slog.e(TAG, "Unable to start pairing server");
            return;
        }

        super.start();
    }

    public void cancelPairing() {
        native_pairing_cancel();
    }

    @Override
    public void onServiceRegistered(NsdServiceInfo serviceInfo) {
        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) {
        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);
    }

    private native int native_pairing_start(String guid, String password);

    private native void native_pairing_cancel();

    private native String native_pairing_wait();
}
+5 −16
Original line number Diff line number Diff line
@@ -91,28 +91,18 @@ static void native_pairing_cancel(JNIEnv* /* env */, jclass /* clazz */) {
    }
}

static jboolean native_pairing_wait(JNIEnv* env, jobject thiz) {
static jstring 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;
        return nullptr;
    }

    // Create a Java string for the public key.
    char* peer_public_key = reinterpret_cast<char*>(sWaiter->peer_info_.data);
    jstring jpublickey = env->NewStringUTF(peer_public_key);
    if (jpublickey == nullptr) {
      return JNI_FALSE;
    }

    // Write to PairingThread.mPublicKey.
    jclass clazz = env->GetObjectClass(thiz);
    jfieldID mPublicKey = env->GetFieldID(clazz, "mPublicKey", "Ljava/lang/String;");
    env->SetObjectField(thiz, mPublicKey, jpublickey);
    return JNI_TRUE;
    return env->NewStringUTF(peer_public_key);
}

// ----------------------------------------------------------------------------
@@ -122,12 +112,11 @@ static const JNINativeMethod gPairingThreadMethods[] = {
        {"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},
        {"native_pairing_wait", "()Ljava/lang/String;", (void*)native_pairing_wait},
};

int register_android_server_AdbDebuggingManager(JNIEnv* env) {
    return jniRegisterNativeMethods(env,
                                    "com/android/server/adb/AdbDebuggingManager$PairingThread",
    return jniRegisterNativeMethods(env, "com/android/server/adb/AdbPairingThread",
                                    gPairingThreadMethods, NELEM(gPairingThreadMethods));
}

+1 −1
Original line number Diff line number Diff line
@@ -873,7 +873,7 @@ public final class AdbDebuggingManagerTest {

    @Test
    public void testPairingThread_MdnsServiceName_RFC6335() {
        assertTrue(isValidMdnsServiceName(AdbDebuggingManager.PairingThread.SERVICE_PROTOCOL));
        assertTrue(isValidMdnsServiceName(AdbPairingThread.SERVICE_PROTOCOL));
    }

    private boolean isValidMdnsServiceName(String name) {