Prune unpaired devs; accept uncatagorized devs

Some devices unpair themselves and needed to stop being listed so user'd
know to fix. And my Nexus 5x is neither a PHONE nor a COMPUTER per BT,
so accept a larger range of BT classes when scanning.
This commit is contained in:
Eric House 2020-10-06 11:43:21 -07:00
parent a9409f8cca
commit fef7d2d544
2 changed files with 103 additions and 42 deletions

View file

@ -22,6 +22,7 @@ package org.eehouse.android.xw4;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@ -42,6 +43,7 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -56,7 +58,7 @@ public class BTInviteDelegate extends InviteDelegate {
private static final boolean ENABLE_FAKER = false; private static final boolean ENABLE_FAKER = false;
private static final int SCAN_SECONDS = 5; private static final int SCAN_SECONDS = 5;
private static Persisted sPersisted; private static Persisted[] sPersistedRef = {null};
private Activity m_activity; private Activity m_activity;
private ProgressBar mProgressBar; private ProgressBar mProgressBar;
@ -168,10 +170,10 @@ public class BTInviteDelegate extends InviteDelegate {
addButtonBar( R.layout.bt_buttons, BUTTONIDS ); addButtonBar( R.layout.bt_buttons, BUTTONIDS );
load( m_activity ); load( m_activity );
if ( sPersisted.empty() ) { if ( sPersistedRef[0].empty() ) {
scan(); scan();
} else { } else {
updateList( sPersisted.pairs ); updateList( sPersistedRef[0].pairs );
} }
} }
@ -205,7 +207,7 @@ public class BTInviteDelegate extends InviteDelegate {
public void run() { public void run() {
hideProgress(); hideProgress();
if ( sPersisted.empty() || 0 == mNDevsThisScan ) { if ( sPersistedRef[0].empty() || 0 == mNDevsThisScan ) {
makeNotAgainBuilder( R.string.not_again_emptybtscan, makeNotAgainBuilder( R.string.not_again_emptybtscan,
R.string.key_notagain_emptybtscan ) R.string.key_notagain_emptybtscan )
.show(); .show();
@ -232,9 +234,9 @@ public class BTInviteDelegate extends InviteDelegate {
String devName = ((TwoStringPair)data).str2; String devName = ((TwoStringPair)data).str2;
String msg = null; String msg = null;
if ( sPersisted.stamps.containsKey( devName ) ) { if ( sPersistedRef[0].stamps.containsKey( devName ) ) {
CharSequence elapsed = DateUtils CharSequence elapsed = DateUtils
.getRelativeTimeSpanString( sPersisted.stamps.get( devName ), .getRelativeTimeSpanString( sPersistedRef[0].stamps.get( devName ),
System.currentTimeMillis(), System.currentTimeMillis(),
DateUtils.SECOND_IN_MILLIS ); DateUtils.SECOND_IN_MILLIS );
msg = getString( R.string.bt_scan_age_fmt, elapsed ); msg = getString( R.string.bt_scan_age_fmt, elapsed );
@ -257,7 +259,7 @@ public class BTInviteDelegate extends InviteDelegate {
private void scan() private void scan()
{ {
if ( ENABLE_FAKER && Utils.nextRandomInt() % 5 == 0 ) { if ( ENABLE_FAKER && Utils.nextRandomInt() % 5 == 0 ) {
sPersisted.add( "00:00:00:00:00:00", "Do Not Invite Me" ); sPersistedRef[0].add( "00:00:00:00:00:00", "Do Not Invite Me" );
} }
int count = BTService.getPairedCount( m_activity ); int count = BTService.getPairedCount( m_activity );
@ -278,10 +280,10 @@ public class BTInviteDelegate extends InviteDelegate {
DbgUtils.assertOnUIThread(); DbgUtils.assertOnUIThread();
++mNDevsThisScan; ++mNDevsThisScan;
sPersisted.add( dev.getAddress(), dev.getName() ); sPersistedRef[0].add( dev.getAddress(), dev.getName() );
store( m_activity ); store( m_activity );
updateList( sPersisted.pairs ); updateList( sPersistedRef[0].pairs );
tryEnable(); tryEnable();
} }
@ -323,23 +325,59 @@ public class BTInviteDelegate extends InviteDelegate {
}, 1000 * inSeconds ); }, 1000 * inSeconds );
} }
private static void removeNotPaired( Persisted prs )
{
Log.d( TAG, "removeNotPaired()" );
BluetoothAdapter adapter = BTService.getAdapterIf();
if ( null != adapter ) {
Set<BluetoothDevice> pairedDevs = adapter.getBondedDevices();
Set<String> paired = new HashSet<>();
for ( BluetoothDevice dev : pairedDevs ) {
Log.d( TAG, "removeNotPaired(): paired dev: %s", dev.getName() );
paired.add( dev.getName() );
}
Set<String> toRemove = new HashSet<>();
for ( TwoStringPair pair : prs.pairs ) {
String name = pair.str2;
if ( ! paired.contains( name ) ) {
Log.d( TAG, "%s no longer paired; removing", name );
toRemove.add( name );
} else {
Log.d( TAG, "%s STILL paired", name );
}
}
if ( ! toRemove.isEmpty() ) {
prs.remove( toRemove );
}
} else {
Log.e( TAG, "removeNotPaired(): adapter null" );
}
}
private synchronized static void load( Context context ) private synchronized static void load( Context context )
{ {
if ( null == sPersisted ) { if ( null == sPersistedRef[0] ) {
Persisted prs;
try { try {
sPersisted = (Persisted)DBUtils.getSerializableFor( context, KEY_PERSIST ); prs = (Persisted)DBUtils.getSerializableFor( context, KEY_PERSIST );
sPersisted.removeNulls(); // clean up earlier mistakes prs.removeNulls(); // clean up earlier mistakes
} catch ( Exception ex ) {} // NPE, de-serialization problems, etc. removeNotPaired( prs );
} catch ( Exception ex ) {
prs = null;
} // NPE, de-serialization problems, etc.
if ( null == sPersisted ) { if ( null == prs ) {
sPersisted = new Persisted(); prs = new Persisted();
} }
sPersistedRef[0] = prs;
} }
} }
private synchronized static void store( Context context ) private synchronized static void store( Context context )
{ {
DBUtils.setSerializableFor( context, KEY_PERSIST, sPersisted ); DBUtils.setSerializableFor( context, KEY_PERSIST, sPersistedRef[0] );
} }
// DlgDelegate.DlgClickNotify interface // DlgDelegate.DlgClickNotify interface
@ -352,11 +390,11 @@ public class BTInviteDelegate extends InviteDelegate {
BTService.openBTSettings( m_activity ); BTService.openBTSettings( m_activity );
break; break;
case CLEAR_ACTION: case CLEAR_ACTION:
sPersisted.remove( getChecked() ); sPersistedRef[0].remove( getChecked() );
store( m_activity ); store( m_activity );
clearChecked(); clearChecked();
updateList( sPersisted.pairs ); updateList( sPersistedRef[0].pairs );
tryEnable(); tryEnable();
break; break;
default: default:
@ -368,7 +406,7 @@ public class BTInviteDelegate extends InviteDelegate {
public static void onHeardFromDev( Context context, BluetoothDevice dev ) public static void onHeardFromDev( Context context, BluetoothDevice dev )
{ {
load( context ); load( context );
sPersisted.add( dev.getAddress(), dev.getName() ); sPersistedRef[0].add( dev.getAddress(), dev.getName() );
store( context ); store( context );
} }
} }

View file

@ -164,21 +164,29 @@ public class BTService extends XWJIService {
private static int s_errCount = 0; private static int s_errCount = 0;
public static BluetoothAdapter getAdapterIf()
{
// Later this will change to include at least a test whether we're
// running as background user account, a situation in which BT crashes
// a lot inside the OS.
return BluetoothAdapter.getDefaultAdapter();
}
public static boolean BTAvailable() public static boolean BTAvailable()
{ {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); BluetoothAdapter adapter = getAdapterIf();
return null != adapter; return null != adapter;
} }
public static boolean BTEnabled() public static boolean BTEnabled()
{ {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); BluetoothAdapter adapter = getAdapterIf();
return null != adapter && adapter.isEnabled(); return null != adapter && adapter.isEnabled();
} }
public static void enable() public static void enable()
{ {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); BluetoothAdapter adapter = getAdapterIf();
if ( null != adapter ) { if ( null != adapter ) {
// Only do this after explicit action from user -- Android guidelines // Only do this after explicit action from user -- Android guidelines
adapter.enable(); adapter.enable();
@ -187,7 +195,7 @@ public class BTService extends XWJIService {
public static String[] getBTNameAndAddress() public static String[] getBTNameAndAddress()
{ {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); BluetoothAdapter adapter = getAdapterIf();
return null == adapter ? null return null == adapter ? null
: new String[] { adapter.getName(), adapter.getAddress() }; : new String[] { adapter.getName(), adapter.getAddress() };
} }
@ -195,7 +203,7 @@ public class BTService extends XWJIService {
public static int getPairedCount( Activity activity ) public static int getPairedCount( Activity activity )
{ {
int result = 0; int result = 0;
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); BluetoothAdapter adapter = getAdapterIf();
if ( null != adapter ) { if ( null != adapter ) {
Set<BluetoothDevice> pairedDevs = adapter.getBondedDevices(); Set<BluetoothDevice> pairedDevs = adapter.getBondedDevices();
result = pairedDevs.size(); result = pairedDevs.size();
@ -212,7 +220,7 @@ public class BTService extends XWJIService {
public static String nameForAddr( String btAddr ) public static String nameForAddr( String btAddr )
{ {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); BluetoothAdapter adapter = getAdapterIf();
return nameForAddr( adapter, btAddr ); return nameForAddr( adapter, btAddr );
} }
@ -346,7 +354,7 @@ public class BTService extends XWJIService {
m_btMsgSink = new BTMsgSink(); m_btMsgSink = new BTMsgSink();
mHandler = new Handler(); mHandler = new Handler();
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); BluetoothAdapter adapter = getAdapterIf();
if ( null != adapter && adapter.isEnabled() ) { if ( null != adapter && adapter.isEnabled() ) {
m_adapter = adapter; m_adapter = adapter;
Log.i( TAG, "onCreate(); bt name = %s; bt addr = %s", Log.i( TAG, "onCreate(); bt name = %s; bt addr = %s",
@ -542,7 +550,7 @@ public class BTService extends XWJIService {
@Override @Override
public void run() { // receive thread public void run() { // receive thread
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); BluetoothAdapter adapter = getAdapterIf();
String appName = XWApp.getAppName( XWApp.getContext() ); String appName = XWApp.getAppName( XWApp.getContext() );
try { try {
@ -685,7 +693,7 @@ public class BTService extends XWJIService {
btAddr = null; btAddr = null;
} }
if ( null == btAddr ) { if ( null == btAddr ) {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); BluetoothAdapter adapter = getAdapterIf();
if ( null != adapter ) { if ( null != adapter ) {
for ( BluetoothDevice dev : adapter.getBondedDevices() ) { for ( BluetoothDevice dev : adapter.getBondedDevices() ) {
// Log.d( TAG, "%s => %s", dev.getName(), dev.getAddress() ); // Log.d( TAG, "%s => %s", dev.getName(), dev.getAddress() );
@ -706,21 +714,36 @@ public class BTService extends XWJIService {
Set<BluetoothDevice> pairedDevs = m_adapter.getBondedDevices(); Set<BluetoothDevice> pairedDevs = m_adapter.getBondedDevices();
Map<BluetoothDevice, PacketAccumulator> pas = new HashMap<>(); Map<BluetoothDevice, PacketAccumulator> pas = new HashMap<>();
for ( BluetoothDevice dev : pairedDevs ) { for ( BluetoothDevice dev : pairedDevs ) {
// Skip things that can't host an Android app // Skip things that can't host an Android app. BUT: one of my
// phones, and presumably lots of others, aren't listed as
// PHONE. So let's try negative testing instead
int clazz = dev.getBluetoothClass().getMajorDeviceClass(); int clazz = dev.getBluetoothClass().getMajorDeviceClass();
if ( Major.PHONE == clazz || Major.COMPUTER == clazz ) { String reject = null;
PacketAccumulator pa = switch ( clazz ) {
new PacketAccumulator( dev.getAddress(), timeoutMS ) case Major.AUDIO_VIDEO:
.addPing( 0 ) reject = "audio"; break;
.setExitWhenEmpty() case Major.HEALTH:
.setLifetimeMS(timeoutMS) reject = "health"; break;
.setService( this ) case Major.IMAGING:
; reject = "imaging"; break;
pas.put( dev, pa ); case Major.TOY:
} else { reject = "toy"; break;
Log.d( TAG, "skipping %s (clazz=%d); not an android device!", case Major.PERIPHERAL:
dev.getName(), clazz ); reject = "peripheral"; break;
} }
if ( null != reject ) {
Log.d( TAG, "sendPings(): %s is a %s; dropping", dev.getName(), reject );
continue;
}
Log.d( TAG, "sendPings(): sending to %s (a %d)", dev.getName(), clazz );
PacketAccumulator pa =
new PacketAccumulator( dev.getAddress(), timeoutMS )
.addPing( 0 )
.setExitWhenEmpty()
.setLifetimeMS(timeoutMS)
.setService( this )
;
pas.put( dev, pa );
} }
for ( BluetoothDevice dev : pas.keySet() ) { for ( BluetoothDevice dev : pas.keySet() ) {
@ -1414,7 +1437,7 @@ public class BTService extends XWJIService {
{ {
Assert.assertFalse( BOGUS_MARSHMALLOW_ADDR.equals( addr ) ); Assert.assertFalse( BOGUS_MARSHMALLOW_ADDR.equals( addr ) );
String result = "<unknown>"; String result = "<unknown>";
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); BluetoothAdapter adapter = getAdapterIf();
if ( null != adapter ) { if ( null != adapter ) {
Set<BluetoothDevice> devs = adapter.getBondedDevices(); Set<BluetoothDevice> devs = adapter.getBondedDevices();
Iterator<BluetoothDevice> iter = devs.iterator(); Iterator<BluetoothDevice> iter = devs.iterator();