Skip to content

Commit 9abcf88

Browse files
committed
Fix #1351
1 parent b7a905e commit 9abcf88

2 files changed

Lines changed: 75 additions & 4 deletions

File tree

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

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,15 +63,16 @@
6363
import android.os.Handler;
6464
import android.os.IBinder;
6565
import android.os.IBinder.DeathRecipient;
66+
import android.os.Looper;
6667
import android.os.Message;
6768
import android.os.Messenger;
6869
import android.os.Parcel;
6970
import android.os.ParcelFileDescriptor;
7071
import android.os.Parcelable;
7172
import android.os.RemoteException;
72-
import android.service.notification.StatusBarNotification;
7373
import android.support.annotation.NonNull;
7474
import android.support.v4.app.NotificationCompat;
75+
import android.util.AndroidRuntimeException;
7576
import android.util.Log;
7677
import android.util.SparseArray;
7778
import android.util.SparseIntArray;
@@ -139,7 +140,7 @@ public class SdlRouterService extends Service{
139140
/**
140141
* <b> NOTE: DO NOT MODIFY THIS UNLESS YOU KNOW WHAT YOU'RE DOING.</b>
141142
*/
142-
protected static final int ROUTER_SERVICE_VERSION_NUMBER = 11;
143+
protected static final int ROUTER_SERVICE_VERSION_NUMBER = 12;
143144

144145
private static final String ROUTER_SERVICE_PROCESS = "com.smartdevicelink.router";
145146

@@ -185,11 +186,18 @@ public class SdlRouterService extends Service{
185186
* Preference location where the service stores known SDL status based on device address
186187
*/
187188
protected static final String SDL_DEVICE_STATUS_SHARED_PREFS = "sdl.device.status";
189+
/**
190+
* Preference location where generic key/values can be stored
191+
*/
192+
protected static final String SDL_ROUTER_SERVICE_PREFS = "sdl.router.service.prefs";
193+
protected static final String KEY_AVOID_NOTIFICATION_CHANNEL_DELETE = "avoidNotificationChannelDelete";
194+
188195

189196

190197

191198
private static boolean connectAsClient = false;
192199
private static boolean closing = false;
200+
private static Thread.UncaughtExceptionHandler routerServiceExceptionHandler = null;
193201

194202
private Handler altTransportTimerHandler, foregroundTimeoutHandler;
195203
private Runnable altTransportTimerRunnable, foregroundTimeoutRunnable;
@@ -1170,6 +1178,9 @@ protected void deployNextRouterService(){
11701178
}
11711179

11721180
public void startUpSequence(){
1181+
1182+
setRouterServiceExceptionHandler();
1183+
11731184
IntentFilter disconnectFilter = new IntentFilter();
11741185
disconnectFilter.addAction(BluetoothDevice.ACTION_CLASS_CHANGED);
11751186
disconnectFilter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
@@ -1196,6 +1207,39 @@ public void startUpSequence(){
11961207
startSequenceComplete= true;
11971208
}
11981209

1210+
/**
1211+
* This method will set a new UncaughtExceptionHandler for the current thread. The only
1212+
* purpose of the custom UncaughtExceptionHandler is to catch the rare occurrence that the
1213+
* a specific mobile device/OS can't properly handle the deletion and creation of the foreground
1214+
* notification channel that is necessary for foreground services after Android Oreo.
1215+
* The new UncaughtExceptionHandler will catch that specific exception and tell the
1216+
* main looper to continue forward. This still leaves the SdlRouterService killed, but prevents
1217+
* an ANR to the app that makes the startForegroundService call. It will set a flag that will
1218+
* prevent the channel from being deleted in the future and therefore avoiding this exception.
1219+
*/
1220+
protected void setRouterServiceExceptionHandler() {
1221+
final Thread.UncaughtExceptionHandler defaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
1222+
if (defaultUncaughtExceptionHandler != routerServiceExceptionHandler) {
1223+
routerServiceExceptionHandler = new Thread.UncaughtExceptionHandler() {
1224+
@Override
1225+
public void uncaughtException(Thread t, Throwable e) {
1226+
if (e != null
1227+
&& e instanceof AndroidRuntimeException
1228+
&& "android.app.RemoteServiceException".equals(e.getClass().getName()) //android.app.RemoteServiceException is a private class
1229+
&& e.getMessage().contains("invalid channel for service notification")) { //This is the message received in the exception for notification channel issues
1230+
1231+
// Set the flag to not delete the notification channel to avoid this exception in the future
1232+
SdlRouterService.this.setSdlRouterServicePrefs(KEY_AVOID_NOTIFICATION_CHANNEL_DELETE, true);
1233+
Looper.loop();
1234+
} else if (defaultUncaughtExceptionHandler != null) { //No other exception should be handled
1235+
defaultUncaughtExceptionHandler.uncaughtException(t, e);
1236+
}
1237+
}
1238+
};
1239+
Thread.setDefaultUncaughtExceptionHandler(routerServiceExceptionHandler);
1240+
}
1241+
}
1242+
11991243

12001244
@SuppressLint({"NewApi", "MissingPermission"})
12011245
@Override
@@ -1528,7 +1572,7 @@ private void exitForeground(){
15281572
if (notificationManager!= null){
15291573
try {
15301574
notificationManager.cancelAll();
1531-
if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
1575+
if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !getBooleanPref(KEY_AVOID_NOTIFICATION_CHANNEL_DELETE,false)) {
15321576
notificationManager.deleteNotificationChannel(SDL_NOTIFICATION_CHANNEL_ID);
15331577
}
15341578
} catch (Exception e) {
@@ -2445,6 +2489,33 @@ protected boolean hasSDLConnected(String address){
24452489
return preferences.contains(address) && preferences.getBoolean(address,false);
24462490
}
24472491

2492+
/**
2493+
* Set specific settings through key/value to the SDL_ROUTER_SERVICE_PREFS
2494+
* @param key the key of the pair to set in the preferences
2495+
* @param value boolean to attach to key in the preferences
2496+
*/
2497+
protected void setSdlRouterServicePrefs(String key, boolean value){
2498+
SharedPreferences preferences = this.getSharedPreferences(SDL_ROUTER_SERVICE_PREFS, Context.MODE_PRIVATE);
2499+
SharedPreferences.Editor editor = preferences.edit();
2500+
editor.putBoolean(key,value);
2501+
editor.commit();
2502+
Log.d(TAG, "Preference set: " + key + " : " + value);
2503+
}
2504+
2505+
/**
2506+
* Retrieves a boolean value for the given key in the SDL_ROUTER_SERVICE_PREFS
2507+
* @param key the string key that will be used to retrieve the boolean value
2508+
* @param defaultValue if they key does not exist or there is no value to be found, this is the
2509+
* value that will be returned
2510+
* @return the value associated with the supplied key or defaultValue if one does not exist
2511+
*/
2512+
protected boolean getBooleanPref(String key, boolean defaultValue){
2513+
SharedPreferences preferences = this.getSharedPreferences(SDL_ROUTER_SERVICE_PREFS, Context.MODE_PRIVATE);
2514+
if(preferences != null){
2515+
return preferences.getBoolean(key, defaultValue);
2516+
}
2517+
return false;
2518+
}
24482519

24492520

24502521
/* ***********************************************************************************************************************************************************************

android/sdl_android/src/main/res/values/sdl.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<resources>
33
<string name="sdl_router_service_version_name" translatable="false">sdl_router_version</string>
44

5-
<integer name="sdl_router_service_version_value">11</integer>
5+
<integer name="sdl_router_service_version_value">12</integer>
66

77
<string name="sdl_router_service_is_custom_name" translatable="false">sdl_custom_router</string>
88
</resources>

0 commit comments

Comments
 (0)