Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
1656b45
Update sdk to 31
bilal-alsharifi Jul 28, 2021
26ccb8b
Update sdk to 31
bilal-alsharifi Jul 28, 2021
664e305
Add the new required android:exported="true" attr
bilal-alsharifi Jul 28, 2021
01ca770
Add FLAG_IMMUTABLE to PendingIntent
bilal-alsharifi Jul 28, 2021
8bb0272
Update BT Permissions and Notification Behavior
Sep 15, 2021
121a51f
Merge branch 'develop' into feature/android_12_fixes
Sep 15, 2021
4cb096b
Add targetApi
Sep 15, 2021
250dffb
RS Notificaiton only if has connected before
Sep 16, 2021
49b8180
Get BT permissions as runtime permissions
Sep 21, 2021
d1a77c9
Merge branch 'develop' into feature/android_12_fixes
Sep 21, 2021
327d8d6
Only set immutable flag if android 12 or newer
RHenigan Oct 27, 2021
a07bd7c
Enable or disable rs based on permission
RHenigan Oct 28, 2021
17d39c2
Toggle Service enabled based on BT permissions
RHenigan Oct 29, 2021
316c673
BroadcastReceiver changes
RHenigan Nov 2, 2021
90f8a4a
Merge branch 'develop' into feature/android_12_fixes
RHenigan Nov 2, 2021
01fae83
Stop service is disabled
RHenigan Nov 2, 2021
8c58dc4
Fix broadcastReceiverBehavior
Nov 3, 2021
9eb0069
Merge branch 'feature/android_12_fixes' of github.com:smartdevicelink…
RHenigan Nov 9, 2021
7bd28d7
Allow USB to start Router Service if no BT perm
RHenigan Nov 12, 2021
e5bb2cf
Fix initCheck in RS
RHenigan Nov 18, 2021
71dc5f6
Add PendingIntents to start services from RS
RHenigan Nov 19, 2021
18133ce
Add Check for TransportType to RS
RHenigan Nov 23, 2021
f866291
Update RS to pick idedal RS
RHenigan Nov 29, 2021
a76ac65
Remove secondPass Logic
RHenigan Nov 30, 2021
71f80f4
Add notification to remind user to allow bluetooth permissions
bilal-alsharifi Nov 30, 2021
997b231
Merge branch 'develop' into feature/android_12_fixes
RHenigan Feb 11, 2022
0dfdb87
Proposal Revision Changes
RHenigan Feb 11, 2022
578a182
Add Bt check for t permissions notificaiton
RHenigan Feb 14, 2022
475e170
Remove BT Scan Permission Check
RHenigan Feb 14, 2022
6fd76ae
Router Service Version increase
RHenigan Feb 14, 2022
3b2f02b
Clean up and comments
RHenigan Feb 14, 2022
48f7cb0
Dev Review Fixes
RHenigan Feb 21, 2022
bbbfb8c
Re-Review changes
RHenigan Feb 23, 2022
2600817
Fix edge case with permissions being revoked
RHenigan Feb 24, 2022
03a0682
Remove accidental ignore
RHenigan Feb 24, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions android/hello_sdl_android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
apply plugin: 'com.android.application'

android {
compileSdkVersion 30
compileSdkVersion 31
defaultConfig {
applicationId "com.sdl.hellosdlandroid"
minSdkVersion 16
targetSdkVersion 30
targetSdkVersion 31
versionCode 1
versionName "1.0"
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
Expand Down
5 changes: 5 additions & 0 deletions android/hello_sdl_android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
package="com.sdl.hellosdlandroid">

<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"
tools:targetApi="31"/>
<uses-permission android:name="android.permission.INTERNET" />
<!-- Required to check if WiFi is enabled -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
Expand All @@ -30,6 +32,7 @@
tools:ignore="DeepLinks">
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Expand All @@ -40,6 +43,7 @@

<activity
android:name="com.smartdevicelink.transport.USBAccessoryAttachmentActivity"
android:exported="true"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
Expand All @@ -52,6 +56,7 @@

<service
android:name="com.sdl.hellosdlandroid.SdlService"
android:exported="true"
android:foregroundServiceType="connectedDevice">
</service>
<service
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,66 @@
package com.sdl.hellosdlandroid;

import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import static android.Manifest.permission.BLUETOOTH_CONNECT;

public class MainActivity extends AppCompatActivity {

private static final int REQUEST_CODE = 200;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//If we are connected to a module we want to start our SdlService


if (BuildConfig.TRANSPORT.equals("MULTI") || BuildConfig.TRANSPORT.equals("MULTI_HB")) {
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !checkPermission()) {
requestPermission();
return;
}
//If we are connected to a module we want to start our SdlService
SdlReceiver.queryForConnectedService(this);
} else if (BuildConfig.TRANSPORT.equals("TCP")) {
} else if (BuildConfig.TRANSPORT.equals("TCP")){
Intent proxyIntent = new Intent(this, SdlService.class);
startService(proxyIntent);
}
}

private boolean checkPermission() {
return PackageManager.PERMISSION_GRANTED == ContextCompat.checkSelfPermission(getApplicationContext(), BLUETOOTH_CONNECT);
}

private void requestPermission() {
ActivityCompat.requestPermissions(this, new String[]{BLUETOOTH_CONNECT}, REQUEST_CODE);
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case REQUEST_CODE:
if (grantResults.length > 0) {

boolean btConnectGranted = grantResults[0] == PackageManager.PERMISSION_GRANTED;

if (btConnectGranted) {
SdlReceiver.queryForConnectedService(this);
}
}
break;
}
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package com.sdl.hellosdlandroid;

import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;

import com.smartdevicelink.transport.SdlBroadcastReceiver;
import com.smartdevicelink.transport.SdlRouterService;
import com.smartdevicelink.transport.TransportConstants;
import com.smartdevicelink.util.DebugTool;

public class SdlReceiver extends SdlBroadcastReceiver {
Expand All @@ -16,13 +18,27 @@ public void onSdlEnabled(Context context, Intent intent) {
DebugTool.logInfo(TAG, "SDL Enabled");
intent.setClass(context, SdlService.class);

// SdlService needs to be foregrounded in Android O and above
// This will prevent apps in the background from crashing when they try to start SdlService
// Because Android O doesn't allow background apps to start background services
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent);
// Starting with Android S SdlService needs to be started from a foreground context.
// We will check the intent for a pendingIntent parcelable extra
// This pendingIntent allows us to start the SdlService from the context of the active router service which is in the foreground
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
PendingIntent pendingIntent = (PendingIntent) intent.getParcelableExtra(TransportConstants.PENDING_INTENT_EXTRA);
if (pendingIntent != null) {
try {
pendingIntent.send(context, 0, intent);
} catch (PendingIntent.CanceledException e) {
e.printStackTrace();
}
}
} else {
context.startService(intent);
// SdlService needs to be foregrounded in Android O and above
// This will prevent apps in the background from crashing when they try to start SdlService
// Because Android O doesn't allow background apps to start background services
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent);
} else {
context.startService(intent);
}
}
}

Expand All @@ -35,4 +51,9 @@ public Class<? extends SdlRouterService> defineLocalSdlRouterClass() {
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent); // Required if overriding this method
}

@Override
public String getSdlServiceName() {
return SdlService.class.getSimpleName();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,13 @@ public void enterForeground() {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager != null) {
notificationManager.createNotificationChannel(channel);
Notification serviceNotification = new Notification.Builder(this, channel.getId())
Notification.Builder builder = new Notification.Builder(this, channel.getId())
.setContentTitle("Connected through SDL")
.setSmallIcon(R.drawable.ic_sdl)
.build();
.setSmallIcon(R.drawable.ic_sdl);
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
builder.setForegroundServiceBehavior(Notification.FOREGROUND_SERVICE_IMMEDIATE);
}
Notification serviceNotification = builder.build();
startForeground(FOREGROUND_SERVICE_ID, serviceNotification);
}
}
Expand Down
4 changes: 2 additions & 2 deletions android/sdl_android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
apply plugin: 'com.android.library'

android {
compileSdkVersion 30
compileSdkVersion 31
defaultConfig {
minSdkVersion 16
targetSdkVersion 30
targetSdkVersion 31
versionCode 21
versionName new File(projectDir.path, ('/../../VERSION')).text.trim()
buildConfigField "String", "VERSION_NAME", '\"' + versionName + '\"'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,15 @@
import java.util.Vector;
import java.util.concurrent.ConcurrentLinkedQueue;

import static android.Manifest.permission.BLUETOOTH_CONNECT;
import static com.smartdevicelink.transport.TransportConstants.FOREGROUND_EXTRA;

public abstract class SdlBroadcastReceiver extends BroadcastReceiver {

private static final String TAG = "Sdl Broadcast Receiver";

protected static final String SDL_ROUTER_SERVICE_CLASS_NAME = "sdlrouterservice";
protected static final int ANDROID_12_ROUTER_SERVICE_VERSION = 16;

public static final String LOCAL_ROUTER_SERVICE_EXTRA = "router_service";
public static final String LOCAL_ROUTER_SERVICE_DID_START_OWN = "did_start";
Expand All @@ -90,6 +92,7 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver {
private static Thread.UncaughtExceptionHandler foregroundExceptionHandler = null;
private static final Object DEVICE_LISTENER_LOCK = new Object();
private static SdlDeviceListener sdlDeviceListener;
private static String serviceName = null;

public int getRouterServiceVersion() {
return SdlRouterService.ROUTER_SERVICE_VERSION_NUMBER;
Expand All @@ -98,6 +101,7 @@ public int getRouterServiceVersion() {
@Override
@CallSuper
public void onReceive(Context context, Intent intent) {

//Log.i(TAG, "Sdl Receiver Activated");
final String action = intent.getAction();
if (action == null) {
Expand Down Expand Up @@ -132,6 +136,10 @@ public void onReceive(Context context, Intent intent) {
device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
}

if (serviceName == null) {
serviceName = getSdlServiceName();
}

boolean didStart = false;
if (localRouterClass == null) {
localRouterClass = defineLocalSdlRouterClass();
Expand Down Expand Up @@ -296,6 +304,18 @@ public void onComplete(Vector<ComponentName> routerServices) {
String routerServicePackage = null;
if (sdlAppInfoList != null && !sdlAppInfoList.isEmpty() && sdlAppInfoList.get(0).getRouterServiceComponentName() != null) {
routerServicePackage = sdlAppInfoList.get(0).getRouterServiceComponentName().getPackageName();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
// If all apps have a RS newer than the Android 12 update, chosen app does not have BT Connect permissions, and more than 1 sdl app is installed
if (!isPreAndroid12RSOnDevice(sdlAppInfoList) && !AndroidTools.isPermissionGranted(BLUETOOTH_CONNECT, context, routerServicePackage) && sdlAppInfoList.size() > 1) {
for (SdlAppInfo appInfo : sdlAppInfoList) {
if (AndroidTools.isPermissionGranted(BLUETOOTH_CONNECT, context, appInfo.getRouterServiceComponentName().getPackageName())) {
//If this app in the list has BT Connect permissions, we want to use that apps RS
routerServicePackage = appInfo.getRouterServiceComponentName().getPackageName();
break;
}
}
}
}
}
DebugTool.logInfo(TAG, ": This app's package: " + myPackage);
DebugTool.logInfo(TAG, ": Router service app's package: " + routerServicePackage);
Expand Down Expand Up @@ -362,13 +382,16 @@ private void wakeRouterServiceAltTransport(Context context) {
}

/**
* This method will set a new UncaughtExceptionHandler for the current thread. The only
* purpose of the custom UncaughtExceptionHandler is to catch the rare occurrence that the
* SdlRouterService can't be started fast enough by the system after calling
* startForegroundService so the onCreate method doesn't get called before the foreground promise
* timer expires. The new UncaughtExceptionHandler will catch that specific exception and tell the
* main looper to continue forward. This still leaves the SdlRouterService killed, but prevents
* an ANR to the app that makes the startForegroundService call.
* This method will set a new UncaughtExceptionHandler for the current thread.
* There are two exceptions we want to catch here. The first exception is the rare
* occurrence that the SdlRouterService can't be started fast enough by the system after calling
* startForegroundService so the onCreate method doesn't get called before the foreground
* promise timer expires. The second is for the instance where the developers "SdlService" class
* can't be started fast enough by the system after calling startForegroundService OR the app
* is unable to start the "SdlService" class because the developer did not export the service
* in the manifest. The new UncaughtExceptionHandler will catch these specific exception and
* tell the main looper to continue forward. This still leaves the respective Service killed,
* but prevents an ANR to the app that makes the startForegroundService call.
*/
static protected void setForegroundExceptionHandler() {
final Thread.UncaughtExceptionHandler defaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
Expand All @@ -378,10 +401,9 @@ static protected void setForegroundExceptionHandler() {
public void uncaughtException(Thread t, Throwable e) {
if (e != null
&& e instanceof AndroidRuntimeException
&& "android.app.RemoteServiceException".equals(e.getClass().getName()) //android.app.RemoteServiceException is a private class
&& ("android.app.RemoteServiceException".equals(e.getClass().getName()) || "android.app.ForegroundServiceDidNotStartInTimeException".equals(e.getClass().getName())) //android.app.RemoteServiceException is a private class
&& e.getMessage() != null
&& e.getMessage().contains("SdlRouterService")) {

&& (e.getMessage().contains("SdlRouterService")) || e.getMessage().contains(serviceName)) {
DebugTool.logInfo(TAG, "Handling failed startForegroundService call");
Looper.loop();
} else if (defaultUncaughtExceptionHandler != null) { //No other exception should be handled
Expand Down Expand Up @@ -598,6 +620,18 @@ public boolean onTransportConnected(Context context, BluetoothDevice bluetoothDe
final List<SdlAppInfo> sdlAppInfoList = AndroidTools.querySdlAppInfo(context, new SdlAppInfo.BestRouterComparator(), vehicleType);
if (sdlAppInfoList != null && !sdlAppInfoList.isEmpty()) {
ComponentName routerService = sdlAppInfoList.get(0).getRouterServiceComponentName();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
// If all apps have a RS newer than the Android 12 update, chosen app does not have BT Connect permissions, and more than 1 sdl app is installed
if (!isPreAndroid12RSOnDevice(sdlAppInfoList) && !AndroidTools.isPermissionGranted(BLUETOOTH_CONNECT, context, routerService.getPackageName()) && sdlAppInfoList.size() > 1) {
for (SdlAppInfo appInfo : sdlAppInfoList) {
if (AndroidTools.isPermissionGranted(BLUETOOTH_CONNECT, context, appInfo.getRouterServiceComponentName().getPackageName())) {
routerService = appInfo.getRouterServiceComponentName();
//If this app in the list has BT Connect permissions, we want to use that apps RS
break;
}
}
}
}
startRouterService(context, routerService, false, bluetoothDevice, true, vehicleType);
}
}
Expand Down Expand Up @@ -634,6 +668,16 @@ public static ComponentName consumeQueuedRouterService() {
}
}

private static boolean isPreAndroid12RSOnDevice(List<SdlAppInfo> sdlAppInfoList) {
for (SdlAppInfo appInfo : sdlAppInfoList) {
//If an installed app RS version is older than Android 12 update version (16)
if (appInfo.getRouterServiceVersion() < ANDROID_12_ROUTER_SERVICE_VERSION) {
return true;
}
}
return false;
}

/**
* We need to define this for local copy of the Sdl Router Service class.
* It will be the main point of connection for Sdl enabled apps
Expand All @@ -656,6 +700,16 @@ public static ComponentName consumeQueuedRouterService() {
*/
public abstract void onSdlEnabled(Context context, Intent intent);


/**
* The developer can override this method to return the name of the class that manages their
* SdlService. This method is used to ensure the SdlBroadcastReceivers exception catcher catches
* the correct exception that may be thrown by the app trying to start their SdlService. If this
* exception is not caught the user may experience an ANR for that app.
*/
public String getSdlServiceName() {
return "SdlService";
}
//public abstract void onSdlDisabled(Context context); //Removing for now until we're able to abstract from developer


Expand Down
Loading