You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android 13下PairWiimote适配修复求助:Wiimote配对异常

修复PairWiimote在Android 13上的配对故障

问题核心分析

Android 13对蓝牙权限、后台蓝牙操作做了更严格的限制,同时蓝牙协议栈的行为变化导致原应用的Socket读取逻辑和配对后初始化流程失效,出现read failed, socket might closed or timeout, read ret: -1错误;Wiimote指示灯无限闪烁说明配对后未完成设备握手。

分步修复方案

1. 补充Android 13+蓝牙权限

Android 12及以上新增BLUETOOTH_CONNECT和BLUETOOTH_SCAN危险权限,必须在Manifest声明并运行时申请:

  • 在AndroidManifest.xml中添加权限:
    <uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" />
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30" />
    
  • 在配对操作前添加权限请求逻辑:
    private static final int REQUEST_BLUETOOTH_PERMISSIONS = 1;
    
    private void checkBluetoothPermissions() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            String[] permissions = {
                Manifest.permission.BLUETOOTH_SCAN,
                Manifest.permission.BLUETOOTH_CONNECT
            };
            if (ContextCompat.checkSelfPermission(this, permissions[0]) != PackageManager.PERMISSION_GRANTED ||
                ContextCompat.checkSelfPermission(this, permissions[1]) != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(this, permissions, REQUEST_BLUETOOTH_PERMISSIONS);
            } else {
                startPairing();
            }
        }
    }
    
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_BLUETOOTH_PERMISSIONS) {
            if (grantResults.length > 0 && allPermissionsGranted(grantResults)) {
                startPairing();
            } else {
                Toast.makeText(this, "需要蓝牙权限才能完成配对", Toast.LENGTH_SHORT).show();
            }
        }
    }
    
    private boolean allPermissionsGranted(int[] grantResults) {
        for (int result : grantResults) {
            if (result != PackageManager.PERMISSION_GRANTED) {
                return false;
            }
        }
        return true;
    }
    

2. 将蓝牙Socket操作移至前台服务

Android 13限制后台进程的蓝牙Socket连接,容易被系统回收导致读取失败。需要把配对后的Socket交互逻辑移到前台服务中:

  • 创建前台服务类:
    public class WiimotePairingService extends Service {
        private static final int NOTIFICATION_ID = 1001;
        private BluetoothSocket mSocket;
        private InputStream mInStream;
        private OutputStream mOutStream;
        private Thread mReadThread;
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            Notification notification = new NotificationCompat.Builder(this, "wiimote_pairing")
                .setContentTitle("Wiimote配对中")
                .setContentText("正在与Wiimote建立连接")
                .setSmallIcon(R.drawable.ic_launcher)
                .build();
            startForeground(NOTIFICATION_ID, notification);
    
            BluetoothDevice device = intent.getParcelableExtra("bluetooth_device");
            initSocket(device);
    
            return START_STICKY;
        }
    
        private void initSocket(BluetoothDevice device) {
            try {
                mSocket = device.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
                mSocket.connect();
                mInStream = mSocket.getInputStream();
                mOutStream = mSocket.getOutputStream();
    
                mReadThread = new Thread(this::readSocketData);
                mReadThread.start();
    
                sendInitCommands();
            } catch (IOException e) {
                e.printStackTrace();
                stopSelf();
            }
        }
    
        private void readSocketData() {
            byte[] buffer = new byte[1024];
            int bytes;
            while (!Thread.currentThread().isInterrupted() && mSocket.isConnected()) {
                try {
                    bytes = mInStream.read(buffer);
                    if (bytes > 0) {
                        processReceivedData(buffer, bytes);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                    break;
                }
            }
            cleanup();
        }
    
        private void sendInitCommands() throws IOException {
            mOutStream.write(new byte[]{0x52, 0x00, 0x01});
            mOutStream.flush();
        }
    
        private void processReceivedData(byte[] buffer, int length) {
            // 参考原应用逻辑处理Wiimote响应,判断是否完成初始化
        }
    
        private void cleanup() {
            try {
                if (mInStream != null) mInStream.close();
                if (mOutStream != null) mOutStream.close();
                if (mSocket != null) mSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            stopForeground(true);
            stopSelf();
        }
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    }
    
  • 在Manifest中注册服务:
    <service android:name=".WiimotePairingService" android:foregroundServiceType="connectedDevice" />
    
  • 启动服务替代原配对逻辑:
    Intent serviceIntent = new Intent(this, WiimotePairingService.class);
    serviceIntent.putExtra("bluetooth_device", selectedDevice);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        startForegroundService(serviceIntent);
    } else {
        startService(serviceIntent);
    }
    

3. 调整Wiimote初始化指令时序

Wiimote无限闪烁说明配对后未完成握手,Android 13蓝牙协议栈对指令时序要求更严格:

  • 按规范顺序发送初始化指令:
    1. 发送0x52 0x00 0x01设置报告模式(按键+加速度)
    2. 发送0x52 0x10 0x00读取Wiimote状态
  • 指令间增加100ms左右延迟,避免协议栈处理不及时导致指令丢失

4. 调试定位问题

  • 使用Android Studio的Bluetooth Profiler捕获配对过程的蓝牙数据包,对比Android 10和13的交互差异,定位未响应的指令
  • 在Socket操作节点添加日志:
    Log.d("WiimotePair", "Socket connected: " + mSocket.isConnected());
    Log.d("WiimotePair", "Received data length: " + bytes);
    

内容的提问来源于stack exchange,提问作者He_ Maxed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 22:41:07