Skip to content

Commit b85d06a

Browse files
Merge pull request #1384 from smartdevicelink/feature/sdl_0301_sdl_device_listener
[SDL-0301] SDL Device Listener
2 parents d147f78 + 1516c07 commit b85d06a

7 files changed

Lines changed: 439 additions & 50 deletions

File tree

android/sdl_android/src/main/java/com/smartdevicelink/transport/MultiplexBluetoothTransport.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import com.smartdevicelink.protocol.SdlPacket;
3636
import com.smartdevicelink.transport.enums.TransportType;
3737
import com.smartdevicelink.transport.utl.TransportRecord;
38+
import com.smartdevicelink.util.DebugTool;
3839

3940
import java.io.IOException;
4041
import java.io.InputStream;
@@ -815,7 +816,9 @@ public void run() {
815816
}
816817
} catch (IOException|NullPointerException e) { // NPE is ONLY to catch error on mmInStream
817818
Log.e(TAG, "Lost connection in the Connected Thread");
818-
e.printStackTrace();
819+
if(DebugTool.isDebugEnabled()){
820+
e.printStackTrace();
821+
}
819822
connectionLost();
820823
break;
821824
}

android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlBroadcastReceiver.java

Lines changed: 137 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
import com.smartdevicelink.R;
5454
import com.smartdevicelink.transport.RouterServiceValidator.TrustedListCallback;
5555
import com.smartdevicelink.transport.enums.TransportType;
56+
import com.smartdevicelink.transport.utl.SdlDeviceListener;
5657
import com.smartdevicelink.util.AndroidTools;
5758
import com.smartdevicelink.util.DebugTool;
5859
import com.smartdevicelink.util.SdlAppInfo;
@@ -85,6 +86,8 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver{
8586
private static final Object QUEUED_SERVICE_LOCK = new Object();
8687
private static ComponentName queuedService = null;
8788
private static Thread.UncaughtExceptionHandler foregroundExceptionHandler = null;
89+
private static final Object DEVICE_LISTENER_LOCK = new Object();
90+
private static SdlDeviceListener sdlDeviceListener;
8891

8992
public int getRouterServiceVersion(){
9093
return SdlRouterService.ROUTER_SERVICE_VERSION_NUMBER;
@@ -190,9 +193,6 @@ public void onListObtained(boolean successful) {
190193
});
191194
}
192195

193-
}else{
194-
//This was previously not hooked up, so let's leave it commented out
195-
//onSdlDisabled(context);
196196
}
197197
return;
198198
}else if(intent.getBooleanExtra(TransportConstants.PING_ROUTER_SERVICE_EXTRA, false)){
@@ -223,52 +223,101 @@ public void onListObtained(boolean successful) {
223223
}
224224
}
225225

226+
/**
227+
* This method will attempt to start the router service.
228+
* @param context to be used to start the service and send broadcasts
229+
* @param componentName the router service that should be started
230+
* @param altTransportWake if the alt transport flag should be set. Only used in debug
231+
* @param device the connected bluetooth device
232+
*/
233+
private static void startRouterService(Context context, ComponentName componentName, boolean altTransportWake, BluetoothDevice device, boolean confirmedDevice) {
234+
if (componentName == null) {
235+
return;
236+
}
237+
238+
Intent serviceIntent = new Intent();
239+
serviceIntent.setComponent(componentName);
240+
241+
if (altTransportWake) {
242+
serviceIntent.setAction(TransportConstants.BIND_REQUEST_TYPE_ALT_TRANSPORT);
243+
}
244+
245+
if (device != null) {
246+
serviceIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
247+
}
248+
249+
if (confirmedDevice) {
250+
serviceIntent.putExtra(TransportConstants.CONFIRMED_SDL_DEVICE, confirmedDevice);
251+
}
252+
253+
try {
254+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
255+
context.startService(serviceIntent);
256+
} else {
257+
serviceIntent.putExtra(FOREGROUND_EXTRA, true);
258+
DebugTool.logInfo("Attempting to startForegroundService - " + System.currentTimeMillis());
259+
setForegroundExceptionHandler(); //Prevent ANR in case the OS takes too long to start the service
260+
context.startForegroundService(serviceIntent);
261+
262+
}
263+
//Make sure to send this out for old apps to close down
264+
SdlRouterService.LocalRouterService self = SdlRouterService.getLocalRouterService(serviceIntent, serviceIntent.getComponent());
265+
Intent restart = new Intent(SdlRouterService.REGISTER_NEWER_SERVER_INSTANCE_ACTION);
266+
restart.putExtra(LOCAL_ROUTER_SERVICE_EXTRA, self);
267+
restart.putExtra(LOCAL_ROUTER_SERVICE_DID_START_OWN, true);
268+
context.sendBroadcast(restart);
269+
270+
} catch (SecurityException e) {
271+
Log.e(TAG, "Security exception, process is bad");
272+
}
273+
}
274+
226275
private boolean wakeUpRouterService(final Context context, final boolean ping, final boolean altTransportWake, final BluetoothDevice device){
227-
new ServiceFinder(context, context.getPackageName(), new ServiceFinder.ServiceFinderCallback() {
276+
new ServiceFinder(context, context.getPackageName(), new ServiceFinder.ServiceFinderCallback() {
228277
@Override
229278
public void onComplete(Vector<ComponentName> routerServices) {
230279
runningBluetoothServicePackage = new Vector<ComponentName>();
231280
runningBluetoothServicePackage.addAll(routerServices);
232281
if (runningBluetoothServicePackage.isEmpty()) {
233282
//If there isn't a service running we should try to start one
234283
//We will try to sort the SDL enabled apps and find the one that's been installed the longest
235-
Intent serviceIntent;
236-
List<SdlAppInfo> sdlAppInfoList = AndroidTools.querySdlAppInfo(context, new SdlAppInfo.BestRouterComparator());
284+
final List<SdlAppInfo> sdlAppInfoList = AndroidTools.querySdlAppInfo(context, new SdlAppInfo.BestRouterComparator());
285+
synchronized (DEVICE_LISTENER_LOCK) {
286+
final boolean sdlDeviceListenerEnabled = SdlDeviceListener.isFeatureSupported(sdlAppInfoList);
287+
if (sdlDeviceListenerEnabled) {
288+
String myPackage = context.getPackageName();
289+
String routerServicePackage = null;
290+
if (sdlAppInfoList != null && !sdlAppInfoList.isEmpty() && sdlAppInfoList.get(0).getRouterServiceComponentName() != null) {
291+
routerServicePackage = sdlAppInfoList.get(0).getRouterServiceComponentName().getPackageName();
292+
}
293+
DebugTool.logInfo(TAG + ": This app's package: " + myPackage);
294+
DebugTool.logInfo(TAG + ": Router service app's package: " + routerServicePackage);
295+
if (myPackage != null && myPackage.equalsIgnoreCase(routerServicePackage)) {
296+
SdlDeviceListener sdlDeviceListener = getSdlDeviceListener(context, device);
297+
if (!sdlDeviceListener.isRunning()) {
298+
sdlDeviceListener.start();
299+
}
300+
} else {
301+
DebugTool.logInfo(TAG + ": Not the app to start the router service nor device listener");
302+
}
303+
return;
304+
}
305+
}
306+
237307
if (sdlAppInfoList != null && !sdlAppInfoList.isEmpty()) {
238-
serviceIntent = new Intent();
239-
serviceIntent.setComponent(sdlAppInfoList.get(0).getRouterServiceComponentName());
308+
startRouterService(context, sdlAppInfoList.get(0).getRouterServiceComponentName(), altTransportWake, device, false);
240309
} else{
241310
Log.d(TAG, "No SDL Router Services found");
242311
Log.d(TAG, "WARNING: This application has not specified its SdlRouterService correctly in the manifest. THIS WILL THROW AN EXCEPTION IN FUTURE RELEASES!!");
243312
return;
244313
}
245-
if (altTransportWake) {
246-
serviceIntent.setAction(TransportConstants.BIND_REQUEST_TYPE_ALT_TRANSPORT);
247-
}
248-
if(device != null){
249-
serviceIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
250-
}
251-
try {
252-
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
253-
context.startService(serviceIntent);
254-
}else {
255-
serviceIntent.putExtra(FOREGROUND_EXTRA, true);
256-
DebugTool.logInfo("Attempting to startForegroundService - " + System.currentTimeMillis());
257-
setForegroundExceptionHandler(); //Prevent ANR in case the OS takes too long to start the service
258-
context.startForegroundService(serviceIntent);
259314

315+
} else { //There are currently running services
316+
if(DebugTool.isDebugEnabled()){
317+
for(ComponentName service : runningBluetoothServicePackage){
318+
DebugTool.logInfo("Currently running router service: " + service.getPackageName());
260319
}
261-
//Make sure to send this out for old apps to close down
262-
SdlRouterService.LocalRouterService self = SdlRouterService.getLocalRouterService(serviceIntent, serviceIntent.getComponent());
263-
Intent restart = new Intent(SdlRouterService.REGISTER_NEWER_SERVER_INSTANCE_ACTION);
264-
restart.putExtra(LOCAL_ROUTER_SERVICE_EXTRA, self);
265-
restart.putExtra(LOCAL_ROUTER_SERVICE_DID_START_OWN, true);
266-
context.sendBroadcast(restart);
267-
268-
} catch (SecurityException e) {
269-
Log.e(TAG, "Security exception, process is bad");
270320
}
271-
} else {
272321
if (altTransportWake) {
273322
wakeRouterServiceAltTransport(context);
274323
return;
@@ -281,15 +330,19 @@ public void onComplete(Vector<ComponentName> routerServices) {
281330
}
282331
}
283332
});
284-
return true;
333+
return true;
285334
}
286335

287336
private void wakeRouterServiceAltTransport(Context context){
288337
Intent serviceIntent = new Intent();
289338
serviceIntent.setAction(TransportConstants.BIND_REQUEST_TYPE_ALT_TRANSPORT);
290339
for (ComponentName compName : runningBluetoothServicePackage) {
291340
serviceIntent.setComponent(compName);
292-
context.startService(serviceIntent);
341+
try{
342+
context.startService(serviceIntent);
343+
} catch (Exception e){
344+
DebugTool.logError("Can't start router service for alt transport");
345+
}
293346

294347
}
295348
}
@@ -329,10 +382,9 @@ public void uncaughtException(Thread t, Throwable e) {
329382
* Determines if an instance of the Router Service is currently running on the device.<p>
330383
* <b>Note:</b> This method no longer works on Android Oreo or newer
331384
* @param context A context to access Android system services through.
332-
* @param pingService Set this to true if you want to make sure the service is up and listening to bluetooth
333385
* @return True if a SDL Router Service is currently running, false otherwise.
334386
*/
335-
private static boolean isRouterServiceRunning(Context context, boolean pingService){
387+
private static boolean isRouterServiceRunning(Context context){
336388
if(context == null){
337389
Log.e(TAG, "Can't look for router service, context supplied was null");
338390
return false;
@@ -356,17 +408,15 @@ private static boolean isRouterServiceRunning(Context context, boolean pingServi
356408
//Log.d(TAG, "Found Service: "+ service.service.getClassName());
357409
if ((service.service.getClassName()).toLowerCase(Locale.US).contains(SDL_ROUTER_SERVICE_CLASS_NAME) && AndroidTools.isServiceExported(context, service.service)) {
358410
runningBluetoothServicePackage.add(service.service); //Store which instance is running
359-
if (pingService) {
360-
pingRouterService(context, service.service.getPackageName(), service.service.getClassName());
361-
}
362411
}
363412
}
364413
return runningBluetoothServicePackage.size() > 0;
365414

366415
}
367416

368417
/**
369-
* Attempts to ping a running router service
418+
* Attempts to ping a running router service. It does call startForegroundService so it is
419+
* important to only call this as a ping if the service is already started.
370420
* @param context A context to access Android system services through.
371421
* @param packageName Package name for service to ping
372422
* @param className Class name for service to ping
@@ -431,7 +481,7 @@ private static void requestTransportStatus(Context context, final SdlRouterStatu
431481
}
432482
return;
433483
}
434-
if((!lookForServices || isRouterServiceRunning(context,false)) && !runningBluetoothServicePackage.isEmpty()){ //So there is a service up, let's see if it's connected
484+
if((!lookForServices || isRouterServiceRunning(context)) && !runningBluetoothServicePackage.isEmpty()){ //So there is a service up, let's see if it's connected
435485
final ConcurrentLinkedQueue<ComponentName> list = new ConcurrentLinkedQueue<ComponentName>(runningBluetoothServicePackage);
436486
final SdlRouterStatusProvider.ConnectedStatusCallback sdlBrCallback = new SdlRouterStatusProvider.ConnectedStatusCallback() {
437487

@@ -471,12 +521,13 @@ public void onListObtained(boolean successful) {
471521
}else{
472522
Log.w(TAG, "Router service isn't running, returning false.");
473523
if(isBluetoothConnected()){
474-
Log.d(TAG, "Bluetooth is connected. Attempting to start Router Service");
524+
Log.d(TAG, "Bluetooth is connected. Attempting to ping Router Service");
475525
Intent serviceIntent = new Intent();
476526
serviceIntent.setAction(TransportConstants.START_ROUTER_SERVICE_ACTION);
477527
serviceIntent.putExtra(TransportConstants.PING_ROUTER_SERVICE_EXTRA, true);
478-
AndroidTools.sendExplicitBroadcast(context,serviceIntent,null);
528+
AndroidTools.sendExplicitBroadcast(context, serviceIntent,null);
479529
}
530+
480531
if(callback!=null){
481532
callback.onConnectionStatusUpdate(false, null,context);
482533
}
@@ -500,6 +551,49 @@ private static boolean isBluetoothConnected() {
500551
return false;
501552
}
502553

554+
555+
private static SdlDeviceListener getSdlDeviceListener(Context context, BluetoothDevice bluetoothDevice){
556+
557+
synchronized (DEVICE_LISTENER_LOCK){
558+
if (sdlDeviceListener == null){
559+
sdlDeviceListener = new SdlDeviceListener(context, bluetoothDevice, new SdlDeviceListener.Callback() {
560+
@Override
561+
public boolean onTransportConnected(Context context, BluetoothDevice bluetoothDevice) {
562+
563+
synchronized (DEVICE_LISTENER_LOCK){
564+
sdlDeviceListener = null;
565+
if(context != null) {
566+
final List<SdlAppInfo> sdlAppInfoList = AndroidTools.querySdlAppInfo(context, new SdlAppInfo.BestRouterComparator());
567+
if(sdlAppInfoList != null && !sdlAppInfoList.isEmpty()) {
568+
ComponentName routerService = sdlAppInfoList.get(0).getRouterServiceComponentName();
569+
startRouterService(context, routerService, false, bluetoothDevice, true);
570+
}
571+
}
572+
}
573+
574+
return false;
575+
}
576+
577+
@Override
578+
public void onTransportDisconnected(BluetoothDevice bluetoothDevice) {
579+
synchronized (DEVICE_LISTENER_LOCK){
580+
sdlDeviceListener = null;
581+
}
582+
}
583+
584+
@Override
585+
public void onTransportError(BluetoothDevice bluetoothDevice) {
586+
synchronized (DEVICE_LISTENER_LOCK){
587+
sdlDeviceListener = null;
588+
}
589+
}
590+
});
591+
}
592+
}
593+
594+
return sdlDeviceListener;
595+
}
596+
503597
public static ComponentName consumeQueuedRouterService(){
504598
synchronized(QUEUED_SERVICE_LOCK){
505599
ComponentName retVal = queuedService;

android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlRouterService.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ public class SdlRouterService extends Service{
140140
/**
141141
* <b> NOTE: DO NOT MODIFY THIS UNLESS YOU KNOW WHAT YOU'RE DOING.</b>
142142
*/
143-
protected static final int ROUTER_SERVICE_VERSION_NUMBER = 12;
143+
protected static final int ROUTER_SERVICE_VERSION_NUMBER = 13;
144144

145145
private static final String ROUTER_SERVICE_PROCESS = "com.smartdevicelink.router";
146146

@@ -1259,7 +1259,9 @@ public int onStartCommand(Intent intent, int flags, int startId) {
12591259
address = device.getAddress();
12601260
}
12611261
}
1262-
int timeout = getNotificationTimeout(address);
1262+
boolean confirmedDevice = intent.getBooleanExtra(TransportConstants.CONFIRMED_SDL_DEVICE, false);
1263+
int timeout = getNotificationTimeout(address, confirmedDevice);
1264+
12631265
enterForeground("Waiting for connection...", timeout, false);
12641266
resetForegroundTimeOut(timeout);
12651267
} else {
@@ -1383,9 +1385,9 @@ private void notifyAltTransportOfClose(int reason){
13831385
* @return the amount of time for a timeout handler to remove the notification.
13841386
*/
13851387
@SuppressLint("MissingPermission")
1386-
private int getNotificationTimeout(String address){
1388+
private int getNotificationTimeout(String address, boolean confirmedDevice){
13871389
if(address != null){
1388-
if(hasSDLConnected(address)){
1390+
if(confirmedDevice || hasSDLConnected(address)){
13891391
return FOREGROUND_TIMEOUT * 2;
13901392
}else if(this.isFirstStatusCheck(address)) {
13911393
// If this is the first time the service has ever connected to this device we want

android/sdl_android/src/main/java/com/smartdevicelink/transport/TransportManager.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import com.smartdevicelink.protocol.SdlPacket;
5050
import com.smartdevicelink.protocol.enums.ControlFrameTags;
5151
import com.smartdevicelink.transport.enums.TransportType;
52+
import com.smartdevicelink.transport.utl.SdlDeviceListener;
5253
import com.smartdevicelink.transport.utl.TransportRecord;
5354
import com.smartdevicelink.util.DebugTool;
5455

@@ -100,7 +101,7 @@ public void onFinishedValidation(boolean valid, ComponentName name) {
100101
if (valid) {
101102
mConfig.service = name;
102103
transport = new TransportBrokerImpl(contextWeakReference.get(), mConfig.appId, mConfig.service);
103-
DebugTool.logInfo("TransportManager start got called; transport=" + transport);
104+
DebugTool.logInfo("TransportManager start was called; transport = " + transport);
104105
if(transport != null){
105106
transport.start();
106107
}
@@ -302,6 +303,16 @@ public synchronized boolean onHardwareConnected(List<TransportRecord> transports
302303
transportStatus.clear();
303304
transportStatus.addAll(transports);
304305
}
306+
//If a bluetooth device has connected, make sure to save the mac address in the case
307+
//this app is asked to host the router service, the app knows to do so immediately on connection.
308+
if(transports != null && transports.size() > 0) {
309+
for (TransportRecord record : transports) {
310+
if(record != null && TransportType.BLUETOOTH.equals(record.getType())) {
311+
SdlDeviceListener.setSDLConnectedStatus(contextWeakReference.get(), record.getAddress(),true);
312+
}
313+
}
314+
}
315+
305316
transportListener.onTransportConnected(transports);
306317
return true;
307318
}

0 commit comments

Comments
 (0)