서비스 검색을 위해 Wi-Fi Direct(P2P) 사용

이 클래스의 첫 번째 강의인 네트워크 서비스 사용 탐색: 로컬 네트워크에 연결된 서비스를 검색하는 방법 그러나 Wi-Fi Direct (P2P) 서비스 검색을 사용하면 근처 기기의 서비스를 검색할 수 있습니다. 네트워크 연결 없이 직접 통신할 수 있습니다 서비스를 광고할 수도 있습니다. 실행할 수 있습니다 이러한 기능은 앱 간의 커뮤니케이션에 도움이 되며, 로컬 네트워크나 핫스팟을 사용할 수 없는 경우에도 작동합니다

이 API 집합의 용도는 네트워크 서비스 검색(Network Service Discovery)과 유사하지만 API는 이전 과정에서 설명한 것처럼 코드로 구현하는 것은 매우 다릅니다. 이 강의에서는 다른 기기에서 사용할 수 있는 서비스를 검색하는 방법을 보여줍니다. Wi-Fi Direct를 사용. 이 강의에서는 Google Kubernetes Engine에만 Wi-Fi Direct API.

매니페스트 설정

Wi-Fi P2P를 사용하려면 CHANGE_WIFI_STATE, ACCESS_WIFI_STATE, ACCESS_FINE_LOCATION, 및 INTERNET 권한을 부여할 수 있습니다 앱이 Android 13 (API 수준 33) 이상을 타겟팅하는 경우 NEARBY_WIFI_DEVICES, 권한을 부여할 수 있습니다. Wi-Fi Direct는 인터넷 연결, 표준 자바 소켓을 사용하며 Android에서 이를 사용합니다. 에는 요청된 권한이 필요합니다.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.android.nsdchat"
    ...

    <uses-permission
        android:required="true"
        android:name="android.permission.ACCESS_WIFI_STATE"/>
    <uses-permission
        android:required="true"
        android:name="android.permission.CHANGE_WIFI_STATE"/>
    <uses-permission
        android:required="true"
        android:name="android.permission.INTERNET"/>
    <!-- If your app targets Android 13 (API level 33)
         or higher, you must declare the NEARBY_WIFI_DEVICES permission. -->
    <uses-permission
        android:name="android.permission.NEARBY_WIFI_DEVICES"
        <!-- If your app derives location information from Wi-Fi APIs,
             don't include the "usesPermissionFlags" attribute. -->
        android:usesPermissionFlags="neverForLocation" />
    <uses-permission
        android:required="true"
        android:name="android.permission.ACCESS_FINE_LOCATION"
        <!-- If any feature in your app relies on precise location information,
             don't include the "maxSdkVersion" attribute. -->
        android:maxSdkVersion="32" />
    ...

위의 권한 외에도 다음 API가 위치 모드 활성화를 요구합니다.

로컬 서비스 추가

지역 서비스를 제공하는 경우 사용할 수 있습니다 로컬 서비스가 등록되면 프레임워크는 피어의 서비스 검색 요청에 자동으로 응답합니다

로컬 서비스를 만들려면 다음 단계를 따르세요.

  1. 만들기 WifiP2pServiceInfo 객체.
  2. 서비스에 관한 정보로 객체를 채웁니다.
  3. addLocalService()를 호출하여 로컬 등록 사용할 수 있습니다

Kotlin

    private fun startRegistration() {
        //  Create a string map containing information about your service.
        val record: Map<String, String> = mapOf(
                "listenport" to SERVER_PORT.toString(),
                "buddyname" to "John Doe${(Math.random() * 1000).toInt()}",
                "available" to "visible"
        )

        // Service information.  Pass it an instance name, service type
        // _protocol._transportlayer , and the map containing
        // information other devices will want once they connect to this one.
        val serviceInfo =
                WifiP2pDnsSdServiceInfo.newInstance("_test", "_presence._tcp", record)

        // Add the local service, sending the service info, network channel,
        // and listener that will be used to indicate success or failure of
        // the request.
        manager.addLocalService(channel, serviceInfo, object : WifiP2pManager.ActionListener {
            override fun onSuccess() {
                // Command successful! Code isn't necessarily needed here,
                // Unless you want to update the UI or add logging statements.
            }

            override fun onFailure(arg0: Int) {
                // Command failed.  Check for P2P_UNSUPPORTED, ERROR, or BUSY
            }
        })
    }

자바

    private void startRegistration() {
        //  Create a string map containing information about your service.
        Map record = new HashMap();
        record.put("listenport", String.valueOf(SERVER_PORT));
        record.put("buddyname", "John Doe" + (int) (Math.random() * 1000));
        record.put("available", "visible");

        // Service information.  Pass it an instance name, service type
        // _protocol._transportlayer , and the map containing
        // information other devices will want once they connect to this one.
        WifiP2pDnsSdServiceInfo serviceInfo =
                WifiP2pDnsSdServiceInfo.newInstance("_test", "_presence._tcp", record);

        // Add the local service, sending the service info, network channel,
        // and listener that will be used to indicate success or failure of
        // the request.
        manager.addLocalService(channel, serviceInfo, new ActionListener() {
            @Override
            public void onSuccess() {
                // Command successful! Code isn't necessarily needed here,
                // Unless you want to update the UI or add logging statements.
            }

            @Override
            public void onFailure(int arg0) {
                // Command failed.  Check for P2P_UNSUPPORTED, ERROR, or BUSY
            }
        });
    }

주변 서비스 찾기

Android는 콜백 메서드를 사용하여 애플리케이션에 이용 가능한 서비스를 알리므로 가장 먼저 해야 할 일은 이를 설정하는 것입니다. 수신 대기할 WifiP2pManager.DnsSdTxtRecordListener 만들기 있습니다. 이 레코드는 원하는 경우 다른 사람이 기기에서 사용할 수 있습니다. 기기가 들어오면 기기 주소와 다른 주소 복사 현재 외부의 데이터 구조에 원하는 관련 정보를 나중에 액세스할 수 있습니다. 다음 예에서는 레코드에 'buddyname'이 포함되어 있음 필드가 사용자 ID로 채워져 있습니다.

Kotlin

private val buddies = mutableMapOf<String, String>()
...
private fun discoverService() {
    /* Callback includes:
     * fullDomain: full domain name: e.g. "printer._ipp._tcp.local."
     * record: TXT record dta as a map of key/value pairs.
     * device: The device running the advertised service.
     */
    val txtListener = DnsSdTxtRecordListener { fullDomain, record, device ->
        Log.d(TAG, "DnsSdTxtRecord available -$record")
        record["buddyname"]?.also {
            buddies[device.deviceAddress] = it
        }
    }
}

자바

final HashMap<String, String> buddies = new HashMap<String, String>();
...
private void discoverService() {
    DnsSdTxtRecordListener txtListener = new DnsSdTxtRecordListener() {
        @Override
        /* Callback includes:
         * fullDomain: full domain name: e.g. "printer._ipp._tcp.local."
         * record: TXT record dta as a map of key/value pairs.
         * device: The device running the advertised service.
         */

        public void onDnsSdTxtRecordAvailable(
                String fullDomain, Map record, WifiP2pDevice device) {
                Log.d(TAG, "DnsSdTxtRecord available -" + record.toString());
                buddies.put(device.deviceAddress, record.get("buddyname"));
            }
        };
}

서비스 정보를 가져오려면 WifiP2pManager.DnsSdServiceResponseListener를 만듭니다. 이 실제 설명과 연결 정보를 수신합니다. 이전 코드 스니펫은 기기 주소를 버디와 페어링하는 Map 객체를 구현했습니다. 있습니다. 서비스 응답 리스너는 이를 사용하여 DNS 레코드를 서비스 정보를 제공합니다 둘 다 리스너가 구현되면 setDnsSdResponseListeners() 메서드를 사용하여 WifiP2pManager에 리스너를 추가합니다.

Kotlin

private fun discoverService() {
    ...

    val servListener = DnsSdServiceResponseListener { instanceName, registrationType, resourceType ->
        // Update the device name with the human-friendly version from
        // the DnsTxtRecord, assuming one arrived.
        resourceType.deviceName = buddies[resourceType.deviceAddress] ?: resourceType.deviceName

        // Add to the custom adapter defined specifically for showing
        // wifi devices.
        val fragment = fragmentManager
                .findFragmentById(R.id.frag_peerlist) as WiFiDirectServicesList
        (fragment.listAdapter as WiFiDevicesAdapter).apply {
            add(resourceType)
            notifyDataSetChanged()
        }

        Log.d(TAG, "onBonjourServiceAvailable $instanceName")
    }

    manager.setDnsSdResponseListeners(channel, servListener, txtListener)
    ...
}

자바

private void discoverService() {
...

    DnsSdServiceResponseListener servListener = new DnsSdServiceResponseListener() {
        @Override
        public void onDnsSdServiceAvailable(String instanceName, String registrationType,
                WifiP2pDevice resourceType) {

                // Update the device name with the human-friendly version from
                // the DnsTxtRecord, assuming one arrived.
                resourceType.deviceName = buddies
                        .containsKey(resourceType.deviceAddress) ? buddies
                        .get(resourceType.deviceAddress) : resourceType.deviceName;

                // Add to the custom adapter defined specifically for showing
                // wifi devices.
                WiFiDirectServicesList fragment = (WiFiDirectServicesList) getFragmentManager()
                        .findFragmentById(R.id.frag_peerlist);
                WiFiDevicesAdapter adapter = ((WiFiDevicesAdapter) fragment
                        .getListAdapter());

                adapter.add(resourceType);
                adapter.notifyDataSetChanged();
                Log.d(TAG, "onBonjourServiceAvailable " + instanceName);
        }
    };

    manager.setDnsSdResponseListeners(channel, servListener, txtListener);
    ...
}

이제 서비스 요청을 만들고 addServiceRequest()를 호출합니다. 이 메서드는 리스너를 사용하여 성공 또는 실패도 보고합니다.

Kotlin

        serviceRequest = WifiP2pDnsSdServiceRequest.newInstance()
        manager.addServiceRequest(
                channel,
                serviceRequest,
                object : WifiP2pManager.ActionListener {
                    override fun onSuccess() {
                        // Success!
                    }

                    override fun onFailure(code: Int) {
                        // Command failed.  Check for P2P_UNSUPPORTED, ERROR, or BUSY
                    }
                }
        )

자바

        serviceRequest = WifiP2pDnsSdServiceRequest.newInstance();
        manager.addServiceRequest(channel,
                serviceRequest,
                new ActionListener() {
                    @Override
                    public void onSuccess() {
                        // Success!
                    }

                    @Override
                    public void onFailure(int code) {
                        // Command failed.  Check for P2P_UNSUPPORTED, ERROR, or BUSY
                    }
                });

마지막으로 discoverServices()를 호출합니다.

Kotlin

        manager.discoverServices(
                channel,
                object : WifiP2pManager.ActionListener {
                    override fun onSuccess() {
                        // Success!
                    }

                    override fun onFailure(code: Int) {
                        // Command failed. Check for P2P_UNSUPPORTED, ERROR, or BUSY
                        when (code) {
                            WifiP2pManager.P2P_UNSUPPORTED -> {
                                Log.d(TAG, "Wi-Fi Direct isn't supported on this device.")
                            }
                        }
                    }
                }
        )

자바

        manager.discoverServices(channel, new ActionListener() {

            @Override
            public void onSuccess() {
                // Success!
            }

            @Override
            public void onFailure(int code) {
                // Command failed.  Check for P2P_UNSUPPORTED, ERROR, or BUSY
                if (code == WifiP2pManager.P2P_UNSUPPORTED) {
                    Log.d(TAG, "Wi-Fi Direct isn't supported on this device.");
                else if(...)
                    ...
            }
        });

모두 잘 되면 과정을 잘 마친 것입니다. 문제가 발생하면 수행한 비동기 호출이 WifiP2pManager.ActionListener를 인수로 사용합니다. 성공 또는 실패를 나타내는 콜백이 제공됩니다. 진단 문제가 발생하면 디버깅 코드를 onFailure()에 넣습니다. 오류 코드 문제에 대한 힌트를 얻을 수 있습니다. 가능한 오류 값은 다음과 같습니다. 그 의미

P2P_UNSUPPORTED
앱을 실행하는 기기에서 Wi-Fi Direct가 지원되지 않습니다.
BUSY
시스템의 작업량이 많아 요청을 처리할 수 없습니다.
ERROR
작업이 내부 오류로 인해 실패했습니다.