当应用程序处于后台时,WifiP2pManager 无法添加本地服务和发现服务 returns WifiP2pManager.Error

WifiP2pManager failed to addLocalService and discoverServices returns WifiP2pManager.Error when application is in background

我是新来的。无论如何,我一直在为这个问题挠头。我创建了一个 运行s WiFiP2pHelper 的服务来发现附近的设备并检索 WifiP2pDnsSdServiceInfo。我在多部手机上测试过,它们似乎都可以正常工作。如果它工作正常,它应该 return 记录如下:

2020-08-12 13:32:23.449 24345-24345/com.example.acts D/WiFi P2P Helper: Service Request Added
2020-08-12 13:32:23.449 24345-24345/com.example.acts D/WiFi P2P Helper: Discover Services Successful
2020-08-12 13:32:23.473 24345-24345/com.example.acts D/WiFi P2P Helper: Local Service Added

现在我在后台为 运行 提供服务(即使应用程序被终止)。它似乎在我测试过的设备上运行良好,除了 Android 10 上的任何 运行s。我怀疑是 Android 10 导致 WiFiP2pManager 或 WiFi P2P 本身不 运行 在后台。但我不完全确定它是否是 Android 10 引起的(我只有 2 Android 10 个设备进行了测试)。它 return 包含以下日志:

2020-08-12 13:33:23.517 24345-24345/com.example.acts D/WiFi P2P Helper: Service Request Added
2020-08-12 13:33:23.517 24345-24345/com.example.acts D/WiFi P2P Helper: Error with P2P 0
2020-08-12 13:33:23.518 24345-24345/com.example.acts D/WiFi P2P Helper: Failed to Add Local Service
2020-08-12 13:33:23.518 24345-24345/com.example.acts E/WiFi P2P Helper: 0

如您所见,“Error with P2P 0”和“Failed to Add Local Service”指向discoverServices和addLocalService。下面是代码。很抱歉把它全部转储在这里。任何帮助将不胜感激!谢谢!

public class WiFiP2PHelper extends Service {
    WifiP2pManager manager;
    WifiP2pManager.Channel channel;
    WifiP2pDnsSdServiceRequest serviceRequest;
    IntentFilter intentFilter;
    String iid;
    private String baseServiceName = "ACTS";
    private String serviceName = "";
    final HashMap<String, String> buddies = new HashMap<String, String>();
    final String TAG = "WiFi P2P Helper";
    Handler handler = new Handler();
    Runnable runnable;
    int delay = 15000;

    @RequiresApi(api = Build.VERSION_CODES.O)
    @Override
    public void onCreate() {
        super.onCreate();
    }

    @RequiresApi(api = Build.VERSION_CODES.O)
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        manager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
        channel = manager.initialize(this, getMainLooper(), null);

        intentFilter = new IntentFilter();
        intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
        intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
        intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
        intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);

        getUser();
        startRegistration();
        discoverService();
        startMyOwnForeground();
        return START_STICKY;
    }

    @RequiresApi(api = Build.VERSION_CODES.O)
    private void startMyOwnForeground() {
        String NOTIFICATION_CHANNEL_ID = "com.example.acts";
        String channelName = "My Background Service";
        NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_NONE);
        chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
        NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        assert manager != null;
        manager.createNotificationChannel(chan);

        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
        Notification notification = notificationBuilder.setOngoing(true)
                .setSmallIcon(R.drawable.ic_launcher_background)
                .setContentTitle("Hello World")
                .setPriority(NotificationManager.IMPORTANCE_MIN)
                .setCategory(Notification.CATEGORY_SERVICE)
                .build();
        startForeground(1333, notification);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    private void getUser() {
        List<User> users = User.getUser();
        int count = users.size();
        if (count > 0) {
            User loggedInUser = users.get(0);
            iid = loggedInUser.Id;
            serviceName = baseServiceName + "_" + loggedInUser.Id;
            Log.e("User", iid);
        } else {
            //uId = "User_Unregistered";
            serviceName = baseServiceName + "_User_Unregistered";
            Log.e("User in else", serviceName);
        }
    }

    public String getWFDMacAddress() {
        try {
            List<NetworkInterface> interfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
            for (NetworkInterface ntwInterface : interfaces) {

                if (ntwInterface.getName().equalsIgnoreCase("p2p0")) {
                    byte[] byteMac = ntwInterface.getHardwareAddress();
                    if (byteMac == null) {
                        return null;
                    }
                    StringBuilder strBuilder = new StringBuilder();
                    for (int i = 0; i < byteMac.length; i++) {
                        strBuilder.append(String.format("%02X:", byteMac[i]));
                    }

                    if (strBuilder.length() > 0) {
                        strBuilder.deleteCharAt(strBuilder.length() - 1);
                    }

                    return strBuilder.toString().toLowerCase();
                }

            }
        } catch (Exception e) {
            Log.d(TAG, e.getMessage());
        }
        return null;
    }

    public int findOpenSocket() throws IOException {
        // Initialize a server socket on the next available port.
        ServerSocket serverSocket = new ServerSocket(0);
        // Store the chosen port.
        int port = serverSocket.getLocalPort();
        serverSocket.close();
        return port;
    }

    private void startRegistration() {
        manager.clearLocalServices(channel, new WifiP2pManager.ActionListener() {
            @Override
            public void onSuccess() {
                Log.e(TAG, "onSuccess: Sucessss");
                int port = 3030;
                try {
                    port = findOpenSocket();
                } catch (IOException e) {
                    e.printStackTrace();
                }

                Map record = new HashMap();
                record.put("listenport", String.valueOf(port));
                record.put("buddyname", iid);
                record.put("available", "visible");

                WifiP2pDnsSdServiceInfo serviceInfo =
                        WifiP2pDnsSdServiceInfo.newInstance(serviceName, "_presence._tcp", record);
       
                manager.addLocalService(channel, serviceInfo, new WifiP2pManager.ActionListener() {
                    @Override
                    public void onSuccess() {
                        Log.d(TAG, "Local Service Added");
                    }

                    @Override
                    public void onFailure(int arg0) {
                        Log.d(TAG, "Failed to Add Local Service");
                        Log.e(TAG, String.valueOf(arg0));
                    }
                });
            }

            @Override
            public void onFailure(int arg0) {
                Log.d(TAG, "P2P Unsupported");
            }
        });
    }

    private void discoverService() {
        WifiP2pManager.DnsSdTxtRecordListener txtListener = new WifiP2pManager.DnsSdTxtRecordListener() {
            @Override
            public void onDnsSdTxtRecordAvailable(
                    String fullDomain, Map record, WifiP2pDevice device) {
                Log.d(TAG, "DnsSdTxtRecord available - " + record.toString());
                buddies.put(device.deviceAddress, (String) record.get("buddyname"));
            }
        };

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

                resourceType.deviceName = buddies
                        .containsKey(resourceType.deviceAddress) ? buddies
                        .get(resourceType.deviceAddress) : resourceType.deviceName;

                String uId = instanceName.replace(baseServiceName + "_", "");

                Log.d(TAG, "Received " + uId);

                /*Discovered device1 = new Discovered();
                device1.discoveredthreeID = uId;
                Discovered.insertTransactionToSQLite(device1);*/
            }
        };

        serviceRequest = WifiP2pDnsSdServiceRequest.newInstance();
        manager.addServiceRequest(channel,
                serviceRequest,
                new WifiP2pManager.ActionListener() {
                    @Override
                    public void onSuccess() {
                        Log.d(TAG, "Service Request Added");
                    }

                    @Override
                    public void onFailure(int code) {
                        Log.d(TAG, "Failed to Add Service Request");
                    }
                });


        manager.setDnsSdResponseListeners(channel, servListener, txtListener);

        manager.discoverServices(channel, new WifiP2pManager.ActionListener() {
            @Override
            public void onSuccess() {
                Log.d(TAG, "Discover Services Successful");
            }

            @Override
            public void onFailure(int code) {
                // Command failed.  Check for P2P_UNSUPPORTED, ERROR, or BUSY
                if (code == WifiP2pManager.P2P_UNSUPPORTED) {
                    Log.d(TAG, "P2P isn't supported on this device.");
                } else if (code == WifiP2pManager.ERROR) {
                    Log.d(TAG, "Error with P2P " + code);
                } else if (code == WifiP2pManager.BUSY) {
                    Log.d(TAG, "P2P is Busy " + code);
                }
            }
        });
    }
}

大家好和我有同样的问题。我找到了解决方案。基本上,Android 10 (API 29) 将定位权限分离为普通定位权限和后台定位权限。您需要让您的应用程序提示后台权限(因为默认情况下不允许)。

将这些添加到您的 AndroidManifest.xml 文件

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

并且在我的 MainActivity onCreate 方法中我添加了一个检查以查看设备是否在 Android 10 (API 29) 上以及权限是否已被授予

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            if ((ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) ||
                    (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_BACKGROUND_LOCATION) != PackageManager.PERMISSION_GRANTED)) {

                ActivityCompat.requestPermissions(this,
                        new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_BACKGROUND_LOCATION}, 1);
            }
        } else {
            if ((ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)) {

                ActivityCompat.requestPermissions(this,
                        new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1);
            }
        }

如果其他人有更好的实现权限检查的方法,请在这里分享。我也想学谢谢!