1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tv.settings.accessories; 18 19 import android.bluetooth.BluetoothAdapter; 20 import android.bluetooth.BluetoothClass; 21 import android.bluetooth.BluetoothDevice; 22 import android.content.Context; 23 import android.text.Html; 24 import android.util.Log; 25 26 import androidx.annotation.Nullable; 27 28 import com.android.settingslib.bluetooth.CachedBluetoothDevice; 29 import com.android.settingslib.bluetooth.LocalBluetoothManager; 30 import com.android.tv.settings.R; 31 32 import java.util.Arrays; 33 import java.util.Collections; 34 import java.util.List; 35 import java.util.concurrent.ExecutionException; 36 import java.util.concurrent.FutureTask; 37 38 /** Provide utilities for Remote & Accessories. */ 39 final class AccessoryUtils { 40 41 public static final String TAG = "AccessoryUtils"; 42 43 private static final int MINOR_MASK = 0b11111100; 44 // Includes any generic keyboards or pointers, and any joystick, game pad, or remote subtypes. 45 private static final int MINOR_REMOTE_MASK = 0b11001100; 46 private static List<String> sKnownDeviceLabels = null; 47 48 /** This allows OEM to easily override the main Service if desired. */ getBluetoothDeviceServiceClass()49 public static Class getBluetoothDeviceServiceClass() { 50 return BluetoothDevicesService.class; 51 } 52 getLocalBluetoothManager(Context context)53 public static LocalBluetoothManager getLocalBluetoothManager(Context context) { 54 final FutureTask<LocalBluetoothManager> localBluetoothManagerFutureTask = 55 new FutureTask<>( 56 // Avoid StrictMode ThreadPolicy violation 57 () -> LocalBluetoothManager.getInstance( 58 context, (c, bluetoothManager) -> { 59 }) 60 ); 61 try { 62 localBluetoothManagerFutureTask.run(); 63 return localBluetoothManagerFutureTask.get(); 64 } catch (InterruptedException | ExecutionException e) { 65 Log.w(TAG, "Error getting LocalBluetoothManager.", e); 66 return null; 67 } 68 } 69 getCachedBluetoothDevice( Context context, BluetoothDevice device)70 public static CachedBluetoothDevice getCachedBluetoothDevice( 71 Context context, BluetoothDevice device) { 72 LocalBluetoothManager localBluetoothManager = getLocalBluetoothManager(context); 73 if (localBluetoothManager != null) { 74 return localBluetoothManager.getCachedDeviceManager().findDevice(device); 75 } 76 return null; 77 } 78 getDefaultBluetoothAdapter()79 public static BluetoothAdapter getDefaultBluetoothAdapter() { 80 final FutureTask<BluetoothAdapter> defaultBluetoothAdapterFutureTask = 81 new FutureTask<>( 82 // Avoid StrictMode ThreadPolicy violation 83 BluetoothAdapter::getDefaultAdapter); 84 try { 85 defaultBluetoothAdapterFutureTask.run(); 86 return defaultBluetoothAdapterFutureTask.get(); 87 } catch (InterruptedException | ExecutionException e) { 88 Log.w(TAG, "Error getting default BluetoothAdapter.", e); 89 return null; 90 } 91 } 92 getLocalName(BluetoothDevice device)93 public static String getLocalName(BluetoothDevice device) { 94 if (device == null) { 95 return null; 96 } 97 return device.getAlias(); 98 } 99 isBluetoothEnabled()100 public static boolean isBluetoothEnabled() { 101 return getDefaultBluetoothAdapter() != null && getDefaultBluetoothAdapter().isEnabled(); 102 } 103 isConnected(BluetoothDevice device)104 public static boolean isConnected(BluetoothDevice device) { 105 if (device == null) { 106 return false; 107 } 108 return device.getBondState() == BluetoothDevice.BOND_BONDED && device.isConnected(); 109 } 110 isBonded(BluetoothDevice device)111 public static boolean isBonded(BluetoothDevice device) { 112 if (device == null) { 113 return false; 114 } 115 return device.getBondState() == BluetoothDevice.BOND_BONDED && !device.isConnected(); 116 } 117 isRemoteClass(BluetoothDevice device)118 public static boolean isRemoteClass(BluetoothDevice device) { 119 if (device == null || device.getBluetoothClass() == null) { 120 return false; 121 } 122 int major = device.getBluetoothClass().getMajorDeviceClass(); 123 int minor = device.getBluetoothClass().getDeviceClass() & MINOR_MASK; 124 return BluetoothClass.Device.Major.PERIPHERAL == major 125 && (minor & ~MINOR_REMOTE_MASK) == 0; 126 } 127 128 // For partner, this will be used to identify official device to omit it in the generic 129 // accessories section since the device's settings will be displayed in partner-implemented 130 // Slice. isKnownDevice(Context context, BluetoothDevice device)131 public static boolean isKnownDevice(Context context, BluetoothDevice device) { 132 if (device == null || device.getName() == null) { 133 return false; 134 } 135 if (sKnownDeviceLabels == null) { 136 if (context == null) { 137 return false; 138 } else { 139 sKnownDeviceLabels = 140 Collections.unmodifiableList( 141 Arrays.asList(context.getResources().getStringArray( 142 R.array.known_bluetooth_device_labels))); 143 // For backward compatibility, the customization name used to be known_remote_labels 144 if (sKnownDeviceLabels.isEmpty()) { 145 sKnownDeviceLabels = Collections.unmodifiableList( 146 Arrays.asList( 147 context.getResources().getStringArray( 148 R.array.known_remote_labels))); 149 } 150 } 151 } 152 153 final String name = device.getName().toLowerCase(); 154 for (String knownLabel : sKnownDeviceLabels) { 155 if (name.contains(knownLabel.toLowerCase())) { 156 return true; 157 } 158 } 159 return false; 160 } 161 162 @Nullable getHtmlEscapedDeviceName(@ullable BluetoothDevice bluetoothDevice)163 static String getHtmlEscapedDeviceName(@Nullable BluetoothDevice bluetoothDevice) { 164 if (bluetoothDevice == null || bluetoothDevice.getName() == null) { 165 return null; 166 } 167 return Html.escapeHtml(bluetoothDevice.getName()); 168 } 169 AccessoryUtils()170 private AccessoryUtils() { 171 // do not allow instantiation 172 } 173 } 174