summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Thrun <jthrun@uievolution.com>2017-10-04 15:17:07 -0700
committerJohn Thrun <jthrun@uievolution.com>2017-10-04 15:17:07 -0700
commit87152cafad8e219a8136a92ac84a8ffceae7c3a6 (patch)
tree67ef6ae5443321cfe303d6488575f9303b082077
parent90a87fac34b1b771763e73afebac0b4a4006a094 (diff)
parentbf2f2b405ca10e777832b32eadfe14ed21d6a74d (diff)
downloadsdl_android-87152cafad8e219a8136a92ac84a8ffceae7c3a6.tar.gz
Merge branch 'develop_f' into feature/haptic_projection
-rw-r--r--.travis.yml9
-rw-r--r--sdl_android/build.gradle2
-rw-r--r--sdl_android/src/androidTest/java/com/smartdevicelink/test/encoder/EncoderUtilsTest.java116
-rw-r--r--sdl_android/src/main/java/com/smartdevicelink/encoder/EncoderUtils.java107
-rw-r--r--sdl_android/src/main/java/com/smartdevicelink/encoder/SdlEncoder.java35
-rw-r--r--sdl_android/src/main/java/com/smartdevicelink/proxy/SdlProxyBase.java2
-rw-r--r--sdl_android/src/main/java/com/smartdevicelink/transport/RouterServiceValidator.java13
-rw-r--r--sdl_android/src/main/java/com/smartdevicelink/transport/SdlBroadcastReceiver.java222
-rw-r--r--sdl_android/src/main/java/com/smartdevicelink/transport/SdlRouterService.java55
-rw-r--r--sdl_android/src/main/java/com/smartdevicelink/transport/TransportBroker.java3
-rw-r--r--sdl_android/src/main/java/com/smartdevicelink/transport/TransportConstants.java7
-rw-r--r--sdl_android/src/main/java/com/smartdevicelink/util/AndroidTools.java29
-rw-r--r--sdl_android/src/main/java/com/smartdevicelink/util/ServiceFinder.java141
13 files changed, 662 insertions, 79 deletions
diff --git a/.travis.yml b/.travis.yml
index 5df5d6198..fb7fc4dbe 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,19 +5,20 @@ android:
components:
# use the latest revision of Android SDK Tools
- tools
- - platform-tools
- tools
+ - platform-tools
- ndk-bundle
# The BuildTools version used by your project
- - build-tools-25.0.2
+ - build-tools-26.0.1
# The SDK version used to compile your project
- - android-22
+ - android-26
+ - android-22 #For emulator
# Specify at least one system image,
# if you need to run emulator(s) during your tests
- sys-img-armeabi-v7a-android-22
- # - sys-img-x86-android-17
+ # - sys-img-x86-android-26
# Android Support Repos
- extra-android-m2repository
diff --git a/sdl_android/build.gradle b/sdl_android/build.gradle
index 599c7ba87..ca93c3073 100644
--- a/sdl_android/build.gradle
+++ b/sdl_android/build.gradle
@@ -1,7 +1,7 @@
apply plugin: 'com.android.library'
android {
- compileSdkVersion 22
+ compileSdkVersion 26
buildToolsVersion "25.0.2"
defaultConfig {
minSdkVersion 8
diff --git a/sdl_android/src/androidTest/java/com/smartdevicelink/test/encoder/EncoderUtilsTest.java b/sdl_android/src/androidTest/java/com/smartdevicelink/test/encoder/EncoderUtilsTest.java
new file mode 100644
index 000000000..083a5dd3f
--- /dev/null
+++ b/sdl_android/src/androidTest/java/com/smartdevicelink/test/encoder/EncoderUtilsTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2017, Xevo Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * * Neither the name of the copyright holder nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.smartdevicelink.test.encoder;
+
+import android.annotation.TargetApi;
+import android.media.MediaFormat;
+import android.os.Build;
+
+import com.smartdevicelink.encoder.EncoderUtils;
+
+import junit.framework.TestCase;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+/**
+ * This is a unit test class for the SmartDeviceLink library project class :
+ * {@link com.smartdevicelink.encoder.EncoderUtils}
+ */
+@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+public class EncoderUtilsTest extends TestCase {
+ public void testGetCodecSpecificDataWithNull() {
+ byte[] result = EncoderUtils.getCodecSpecificData(null);
+ assertNull(result);
+ }
+
+ public void testGetCodecSpecificDataForAVC() {
+ // example of SPS NAL unit with 4-byte start code
+ byte[] sps = new byte[] {
+ 0x00, 0x00, 0x00, 0x01,
+ 0x67, 0x42, (byte)0xC0, 0x0A, (byte)0xA6, 0x11, 0x11, (byte)0xE8,
+ 0x40, 0x00, 0x00, (byte)0xFA, 0x40, 0x00, 0x3A, (byte)0x98,
+ 0x23, (byte)0xC4, (byte)0x89, (byte)0x84, 0x60
+ };
+ // example of PPS NAL unit with 4-byte start code
+ byte[] pps = new byte[] {
+ 0x00, 0x00, 0x00, 0x01,
+ 0x68, (byte)0xC8, 0x42, 0x0F, 0x13, 0x20
+ };
+
+ ByteBuffer spsByteBuffer = ByteBuffer.allocate(sps.length);
+ spsByteBuffer.put(sps);
+ spsByteBuffer.flip();
+
+ ByteBuffer ppsByteBuffer = ByteBuffer.allocate(pps.length);
+ ppsByteBuffer.put(pps);
+ ppsByteBuffer.flip();
+
+ MediaFormat format = MediaFormat.createVideoFormat("video/avc", 16, 16);
+ format.setByteBuffer("csd-0", spsByteBuffer);
+ format.setByteBuffer("csd-1", ppsByteBuffer);
+
+ byte[] result = EncoderUtils.getCodecSpecificData(format);
+ assertNotNull(result);
+
+ byte[] expected = new byte[sps.length + pps.length];
+ System.arraycopy(sps, 0, expected, 0, sps.length);
+ System.arraycopy(pps, 0, expected, sps.length, pps.length);
+ assertTrue("Output codec specific data doesn't match", Arrays.equals(expected, result));
+ }
+
+ public void testGetCodecSpecificDataWithInvalidAVCData() {
+ // testing an error case when the encoder emits SPS only (which should not happen)
+ byte[] sps = new byte[] {
+ 0x00, 0x00, 0x00, 0x01,
+ 0x67, 0x42, (byte)0xC0, 0x0A, (byte)0xA6, 0x11, 0x11, (byte)0xE8,
+ 0x40, 0x00, 0x00, (byte)0xFA, 0x40, 0x00, 0x3A, (byte)0x98,
+ 0x23, (byte)0xC4, (byte)0x89, (byte)0x84, 0x60
+ };
+
+ ByteBuffer spsByteBuffer = ByteBuffer.allocate(sps.length);
+ spsByteBuffer.put(sps);
+ spsByteBuffer.flip();
+
+ MediaFormat format = MediaFormat.createVideoFormat("video/avc", 16, 16);
+ format.setByteBuffer("csd-0", spsByteBuffer);
+ // no PPS
+
+ byte[] result = EncoderUtils.getCodecSpecificData(format);
+ assertNull(result);
+ }
+
+ public void testGetCodecSpecificDataForUnknownCodec() {
+ MediaFormat format = MediaFormat.createVideoFormat("video/raw", 16, 16);
+ byte[] result = EncoderUtils.getCodecSpecificData(format);
+ assertNull("For unsupported codec, getCodecSpecificData should return null", result);
+ }
+}
diff --git a/sdl_android/src/main/java/com/smartdevicelink/encoder/EncoderUtils.java b/sdl_android/src/main/java/com/smartdevicelink/encoder/EncoderUtils.java
new file mode 100644
index 000000000..362564da8
--- /dev/null
+++ b/sdl_android/src/main/java/com/smartdevicelink/encoder/EncoderUtils.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2017, Xevo Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * * Neither the name of the copyright holder nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.smartdevicelink.encoder;
+
+import android.annotation.TargetApi;
+import android.media.MediaFormat;
+import android.os.Build;
+import android.util.Log;
+
+import java.nio.ByteBuffer;
+
+@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+public final class EncoderUtils {
+ private final static String TAG = "EncoderUtils";
+
+ /**
+ * Extracts codec-specific data from MediaFormat instance
+ *
+ * Currently, only AVC is supported.
+ *
+ * @param format MediaFormat instance retrieved from MediaCodec
+ * @return byte array containing codec-specific data, or null if an error occurred
+ */
+ public static byte[] getCodecSpecificData(MediaFormat format) {
+ if (format == null) {
+ return null;
+ }
+
+ String name = format.getString(MediaFormat.KEY_MIME);
+ if (name == null) {
+ return null;
+ }
+
+ // same as MediaFormat.MIMETYPE_VIDEO_AVC but it requires API level 21
+ if (name.equals("video/avc")) {
+ return getAVCCodecSpecificData(format);
+ } else {
+ Log.w(TAG, "Retrieving codec-specific data for " + name + " is not supported");
+ return null;
+ }
+ }
+
+ /**
+ * Extracts H.264 codec-specific data (SPS and PPS) from MediaFormat instance
+ *
+ * The codec-specific data is in byte-stream format; 4-byte start codes (0x00 0x00 0x00 0x01)
+ * are added in front of SPS and PPS NAL units.
+ *
+ * @param format MediaFormat instance retrieved from MediaCodec
+ * @return byte array containing codec-specific data, or null if an error occurred
+ */
+ private static byte[] getAVCCodecSpecificData(MediaFormat format) {
+ // For H.264, "csd-0" contains SPS and "csd-1" contains PPS. Refer to the documentation
+ // of MediaCodec.
+ if (!(format.containsKey("csd-0") && format.containsKey("csd-1"))) {
+ Log.w(TAG, "H264 codec specific data not found");
+ return null;
+ }
+
+ ByteBuffer sps = format.getByteBuffer("csd-0");
+ int spsLen = sps.remaining();
+ ByteBuffer pps = format.getByteBuffer("csd-1");
+ int ppsLen = pps.remaining();
+
+ byte[] output = new byte[spsLen + ppsLen];
+ try {
+ sps.get(output, 0, spsLen);
+ pps.get(output, spsLen, ppsLen);
+ } catch (Exception e) {
+ // should not happen
+ Log.w(TAG, "Error while copying H264 codec specific data: " + e);
+ return null;
+ }
+
+ return output;
+ }
+
+ private EncoderUtils() {}
+}
diff --git a/sdl_android/src/main/java/com/smartdevicelink/encoder/SdlEncoder.java b/sdl_android/src/main/java/com/smartdevicelink/encoder/SdlEncoder.java
index dccabff0b..196f5d98c 100644
--- a/sdl_android/src/main/java/com/smartdevicelink/encoder/SdlEncoder.java
+++ b/sdl_android/src/main/java/com/smartdevicelink/encoder/SdlEncoder.java
@@ -9,13 +9,16 @@ import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.os.Build;
+import android.util.Log;
import android.view.Surface;
import com.smartdevicelink.proxy.interfaces.IVideoStreamListener;
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public class SdlEncoder {
-
+
+ private static final String TAG = "SdlEncoder";
+
// parameters for the encoder
private static final String _MIME_TYPE = "video/avc"; // H.264/AVC video
// private static final String MIME_TYPE = "video/mp4v-es"; //MPEG4 video
@@ -32,7 +35,9 @@ public class SdlEncoder {
// allocate one of these up front so we don't need to do it every time
private MediaCodec.BufferInfo mBufferInfo;
-
+
+ // Codec-specific data (SPS and PPS)
+ private byte[] mH264CodecSpecificData = null;
public SdlEncoder () {
}
@@ -120,6 +125,7 @@ public class SdlEncoder {
}
mOutputStream = null;
}
+ mH264CodecSpecificData = null;
}
/**
@@ -153,8 +159,33 @@ public class SdlEncoder {
// not expected for an encoder
encoderOutputBuffers = mEncoder.getOutputBuffers();
} else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+ if (mH264CodecSpecificData == null) {
+ MediaFormat format = mEncoder.getOutputFormat();
+ mH264CodecSpecificData = EncoderUtils.getCodecSpecificData(format);
+ } else {
+ Log.w(TAG, "Output format change notified more than once, ignoring.");
+ }
} else if (encoderStatus < 0) {
} else {
+ if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
+ // If we already retrieve codec specific data via OUTPUT_FORMAT_CHANGED event,
+ // we do not need this data.
+ if (mH264CodecSpecificData != null) {
+ mBufferInfo.size = 0;
+ } else {
+ Log.i(TAG, "H264 codec specific data not retrieved yet.");
+ }
+ }
+ // append SPS and PPS in front of every IDR NAL unit
+ if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0
+ && mBufferInfo.size != 0
+ && mH264CodecSpecificData != null) {
+ try {
+ mOutputStream.write(mH264CodecSpecificData, 0,
+ mH264CodecSpecificData.length);
+ } catch (Exception e) {}
+ }
+
if (mBufferInfo.size != 0) {
byte[] dataToWrite = new byte[mBufferInfo.size];
encoderOutputBuffers[encoderStatus].get(dataToWrite,
diff --git a/sdl_android/src/main/java/com/smartdevicelink/proxy/SdlProxyBase.java b/sdl_android/src/main/java/com/smartdevicelink/proxy/SdlProxyBase.java
index 5c27a3ea8..dc284dbae 100644
--- a/sdl_android/src/main/java/com/smartdevicelink/proxy/SdlProxyBase.java
+++ b/sdl_android/src/main/java/com/smartdevicelink/proxy/SdlProxyBase.java
@@ -3446,7 +3446,7 @@ public abstract class SdlProxyBase<proxyListenerType extends IProxyListenerBase>
// Test if SdlConnection is null
synchronized(CONNECTION_REFERENCE_LOCK) {
- if (getIsConnected()) {
+ if (!getIsConnected()) {
SdlTrace.logProxyEvent("Application attempted to send and RPCRequest without a connected transport.", SDL_LIB_TRACE_KEY);
throw new SdlException("There is no valid connection to SDL. sendRPCRequest cannot be called until SDL has been connected.", SdlExceptionCause.SDL_UNAVAILABLE);
}
diff --git a/sdl_android/src/main/java/com/smartdevicelink/transport/RouterServiceValidator.java b/sdl_android/src/main/java/com/smartdevicelink/transport/RouterServiceValidator.java
index 66e415f29..92dce9dbe 100644
--- a/sdl_android/src/main/java/com/smartdevicelink/transport/RouterServiceValidator.java
+++ b/sdl_android/src/main/java/com/smartdevicelink/transport/RouterServiceValidator.java
@@ -22,6 +22,7 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.os.Build;
import android.util.Log;
import com.smartdevicelink.util.HttpRequestTask;
@@ -117,7 +118,7 @@ public class RouterServiceValidator {
if(this.service != null){
Log.d(TAG, "Supplied service name of " + this.service.getClassName());
- if(!isServiceRunning(context,this.service)){
+ if(Build.VERSION.SDK_INT < Build.VERSION_CODES.O && !isServiceRunning(context,this.service)){
//This means our service isn't actually running, so set to null. Hopefully we can find a real router service after this.
service = null;
Log.w(TAG, "Supplied service is not actually running.");
@@ -129,11 +130,17 @@ public class RouterServiceValidator {
}
}
if(this.service == null){
- this.service= componentNameForServiceRunning(pm); //Change this to an array if multiple services are started?
- if(this.service == null){ //if this is still null we know there is no service running so we can return false
+ if(Build.VERSION.SDK_INT < Build.VERSION_CODES.O ) {
+ this.service = componentNameForServiceRunning(pm); //Change this to an array if multiple services are started?
+ if (this.service == null) { //if this is still null we know there is no service running so we can return false
+ wakeUpRouterServices();
+ return false;
+ }
+ }else{
wakeUpRouterServices();
return false;
}
+
}
//Log.d(TAG, "Checking app package: " + service.getClassName());
diff --git a/sdl_android/src/main/java/com/smartdevicelink/transport/SdlBroadcastReceiver.java b/sdl_android/src/main/java/com/smartdevicelink/transport/SdlBroadcastReceiver.java
index 7312e78ef..94bb3c51e 100644
--- a/sdl_android/src/main/java/com/smartdevicelink/transport/SdlBroadcastReceiver.java
+++ b/sdl_android/src/main/java/com/smartdevicelink/transport/SdlBroadcastReceiver.java
@@ -1,22 +1,39 @@
package com.smartdevicelink.transport;
-import java.util.List;
-import java.util.Locale;
-import java.util.Vector;
-import java.util.concurrent.ConcurrentLinkedQueue;
-
-import com.smartdevicelink.util.AndroidTools;
-import com.smartdevicelink.transport.RouterServiceValidator.TrustedListCallback;
-
+import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningServiceInfo;
+import android.app.usage.UsageStats;
+import android.app.usage.UsageStatsManager;
import android.bluetooth.BluetoothAdapter;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Build;
import android.util.Log;
+import com.smartdevicelink.transport.RouterServiceValidator.TrustedListCallback;
+import com.smartdevicelink.util.AndroidTools;
+import com.smartdevicelink.util.ServiceFinder;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Vector;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import static com.smartdevicelink.transport.TransportConstants.BIND_LOCATION_CLASS_NAME_EXTRA;
+import static com.smartdevicelink.transport.TransportConstants.BIND_LOCATION_PACKAGE_NAME_EXTRA;
+import static com.smartdevicelink.transport.TransportConstants.FOREGROUND_EXTRA;
+import static com.smartdevicelink.transport.TransportConstants.SEND_PACKET_TO_APP_LOCATION_EXTRA_NAME;
+
public abstract class SdlBroadcastReceiver extends BroadcastReceiver{
private static final String TAG = "Sdl Broadcast Receiver";
@@ -129,10 +146,10 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver{
return;
}
}
-
+ Log.d(TAG, "Check for local router");
if(localRouterClass!=null){ //If there is a supplied router service lets run some logic regarding starting one
- if(!didStart){
+ if(!didStart){Log.d(TAG, "attempting to wake up router service");
didStart = wakeUpRouterService(context, true,false);
}
@@ -147,42 +164,109 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver{
context.sendBroadcast(restart);
}
}
-
- private boolean wakeUpRouterService(Context context, boolean ping, boolean altTransportWake){
- if(!isRouterServiceRunning(context, ping)){
- //If there isn't a service running we should try to start one
- //The under class should have implemented this....
-
- //So let's start up our service since no copy is running
- Intent serviceIntent = new Intent(context, localRouterClass);
- if(altTransportWake){
- serviceIntent.setAction(TransportConstants.BIND_REQUEST_TYPE_ALT_TRANSPORT);
- }
- try {
- context.startService(serviceIntent);
- }catch (SecurityException e){
- Log.e(TAG, "Security exception, process is bad");
- return false; // Let's exit, we can't start the service
- }
- return true;
- }else{
- if(altTransportWake && runningBluetoothServicePackage!=null && runningBluetoothServicePackage.size()>0){
- Intent serviceIntent = new Intent();
- serviceIntent.setAction(TransportConstants.BIND_REQUEST_TYPE_ALT_TRANSPORT);
- //context.startService(serviceIntent);
- for(ComponentName compName: runningBluetoothServicePackage){
- serviceIntent.setComponent(compName);
- context.startService(serviceIntent);
- }
- return true;
- }
- return false;
- }
+ @TargetApi(Build.VERSION_CODES.O)
+ private boolean wakeUpRouterService(final Context context, final boolean ping, final boolean altTransportWake){
+ if(Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
+ if (!isRouterServiceRunning(context, ping)) {
+ //If there isn't a service running we should try to start one
+ //The under class should have implemented this....
+ Log.d(TAG, "No router service running, starting ours");
+ //So let's start up our service since no copy is running
+ Intent serviceIntent = new Intent(context, localRouterClass);
+ if (altTransportWake) {
+ serviceIntent.setAction(TransportConstants.BIND_REQUEST_TYPE_ALT_TRANSPORT);
+ }
+ try {
+ context.startService(serviceIntent);
+ } catch (SecurityException e) {
+ Log.e(TAG, "Security exception, process is bad");
+ return false; // Let's exit, we can't start the service
+ }
+ return true;
+ } else {
+ if (altTransportWake && runningBluetoothServicePackage != null && runningBluetoothServicePackage.size() > 0) {
+ wakeRouterServiceAltTransport(context);
+ return true;
+ }
+ return false;
+ }
+ }else{ //We are android Oreo or newer
+ ServiceFinder finder = new ServiceFinder(context, context.getPackageName(), new ServiceFinder.ServiceFinderCallback() {
+ @Override
+ public void onComplete(Vector<ComponentName> routerServices) {
+ runningBluetoothServicePackage = new Vector<ComponentName>();
+ runningBluetoothServicePackage.addAll(routerServices);
+ if (runningBluetoothServicePackage.isEmpty()) {
+ //If there isn't a service running we should try to start one
+ //We will try to sort the SDL enabled apps and find the one that's been installed the longest
+ Intent serviceIntent;
+ final PackageManager packageManager = context.getPackageManager();
+ Vector<ResolveInfo> apps = new Vector(AndroidTools.getSdlEnabledApps(context, "").values()); //we want our package
+ if (apps != null && !apps.isEmpty()) {
+ Collections.sort(apps, new Comparator<ResolveInfo>() {
+ @Override
+ public int compare(ResolveInfo resolveInfo, ResolveInfo t1) {
+ try {
+ PackageInfo thisPack = packageManager.getPackageInfo(resolveInfo.activityInfo.packageName, 0);
+ PackageInfo itPack = packageManager.getPackageInfo(t1.activityInfo.packageName, 0);
+ if (thisPack.lastUpdateTime < itPack.lastUpdateTime) {
+ return -1;
+ } else if (thisPack.lastUpdateTime > itPack.lastUpdateTime) {
+ return 1;
+ }
+
+ } catch (PackageManager.NameNotFoundException e) {
+ e.printStackTrace();
+ }
+ return 0;
+ }
+ });
+ String packageName = apps.get(0).activityInfo.packageName;
+ serviceIntent = new Intent();
+ serviceIntent.setComponent(new ComponentName(packageName, packageName +".SdlRouterService"));
+ } else{
+ Log.d(TAG, "No router service running, starting ours");
+ //So let's start up our service since no copy is running
+ serviceIntent = new Intent(context, localRouterClass);
+
+ }
+ if (altTransportWake) {
+ serviceIntent.setAction(TransportConstants.BIND_REQUEST_TYPE_ALT_TRANSPORT);
+ }
+ try {
+ serviceIntent.putExtra(FOREGROUND_EXTRA, true);
+ context.startForegroundService(serviceIntent);
+
+ } catch (SecurityException e) {
+ Log.e(TAG, "Security exception, process is bad");
+ }
+ } else {
+ if (altTransportWake && runningBluetoothServicePackage != null && runningBluetoothServicePackage.size() > 0) {
+ wakeRouterServiceAltTransport(context);
+ return;
+ }
+ return;
+ }
+ }
+ });
+ return true;
+ }
+ }
+
+ private void wakeRouterServiceAltTransport(Context context){
+ Intent serviceIntent = new Intent();
+ serviceIntent.setAction(TransportConstants.BIND_REQUEST_TYPE_ALT_TRANSPORT);
+ for (ComponentName compName : runningBluetoothServicePackage) {
+ serviceIntent.setComponent(compName);
+ context.startService(serviceIntent);
+
+ }
}
/**
- * Determines if an instance of the Router Service is currently running on the device.
+ * Determines if an instance of the Router Service is currently running on the device.<p>
+ * <b>Note:</b> This method no longer works on Android Oreo or newer
* @param context A context to access Android system services through.
* @param pingService Set this to true if you want to make sure the service is up and listening to bluetooth
* @return True if a SDL Router Service is currently running, false otherwise.
@@ -192,31 +276,30 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver{
Log.e(TAG, "Can't look for router service, context supplied was null");
return false;
}
- ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
- if(runningBluetoothServicePackage==null){
+ if (runningBluetoothServicePackage == null) {
runningBluetoothServicePackage = new Vector<ComponentName>();
- }else{
+ } else {
runningBluetoothServicePackage.clear();
}
+ ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+ manager.getRunningAppProcesses();
List<RunningServiceInfo> runningServices = null;
- try{
+ try {
runningServices = manager.getRunningServices(Integer.MAX_VALUE);
- }catch(NullPointerException e){
+ } catch (NullPointerException e) {
Log.e(TAG, "Can't get list of running services");
return false;
}
- for (RunningServiceInfo service : runningServices) {
+ for (RunningServiceInfo service : runningServices) {
//We will check to see if it contains this name, should be pretty specific
- //Log.d(TAG, "Found Service: "+ service.service.getClassName());
- if ((service.service.getClassName()).toLowerCase(Locale.US).contains(SDL_ROUTER_SERVICE_CLASS_NAME) && AndroidTools.isServiceExported(context, service.service)) {
-
- runningBluetoothServicePackage.add(service.service); //Store which instance is running
- if(pingService){
+ //Log.d(TAG, "Found Service: "+ service.service.getClassName());
+ if ((service.service.getClassName()).toLowerCase(Locale.US).contains(SDL_ROUTER_SERVICE_CLASS_NAME) && AndroidTools.isServiceExported(context, service.service)) {
+ runningBluetoothServicePackage.add(service.service); //Store which instance is running
+ if (pingService) {
pingRouterService(context, service.service.getPackageName(), service.service.getClassName());
- }
- }
- }
-
+ }
+ }
+ }
return runningBluetoothServicePackage.size() > 0;
}
@@ -241,33 +324,46 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver{
// This service could not be started
}
}
-
+
/**
* This call will reach out to all SDL related router services to check if they're connected. If a the router service is connected, it will react by pinging all clients. This receiver will then
* receive that ping and if the router service is trusted, the onSdlEnabled method will be called.
* @param context
*/
- public static void queryForConnectedService(Context context){
+ public static void queryForConnectedService(final Context context){
//Leverage existing call. Include ping bit
- requestTransportStatus(context,null,true);
+ if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
+ ServiceFinder finder = new ServiceFinder(context, context.getPackageName(), new ServiceFinder.ServiceFinderCallback() {
+ @Override
+ public void onComplete(Vector<ComponentName> routerServices) {
+ runningBluetoothServicePackage = new Vector<ComponentName>();
+ runningBluetoothServicePackage.addAll(routerServices);
+ requestTransportStatus(context,null,true,false);
+ }
+ });
+
+ }else{
+ requestTransportStatus(context,null,true,true);
+ }
}
/**
* If a Router Service is running, this method determines if that service is connected to a device over some form of transport.
* @param context A context to access Android system services through. If null is passed, this will always return false
* @param callback Use this callback to find out if the router service is connected or not.
*/
+ @Deprecated
public static void requestTransportStatus(Context context, final SdlRouterStatusProvider.ConnectedStatusCallback callback){
- requestTransportStatus(context,callback,false);
+ requestTransportStatus(context,callback,false, true);
}
- private static void requestTransportStatus(Context context, final SdlRouterStatusProvider.ConnectedStatusCallback callback, final boolean triggerRouterServicePing){
+ private static void requestTransportStatus(Context context, final SdlRouterStatusProvider.ConnectedStatusCallback callback, final boolean triggerRouterServicePing, final boolean lookForServices){
if(context == null){
if(callback!=null){
callback.onConnectionStatusUpdate(false, null,context);
}
return;
}
- if(isRouterServiceRunning(context,false) && !runningBluetoothServicePackage.isEmpty()){ //So there is a service up, let's see if it's connected
+ if((!lookForServices || isRouterServiceRunning(context,false)) && !runningBluetoothServicePackage.isEmpty()){ //So there is a service up, let's see if it's connected
final ConcurrentLinkedQueue<ComponentName> list = new ConcurrentLinkedQueue<ComponentName>(runningBluetoothServicePackage);
final SdlRouterStatusProvider.ConnectedStatusCallback sdlBrCallback = new SdlRouterStatusProvider.ConnectedStatusCallback() {
diff --git a/sdl_android/src/main/java/com/smartdevicelink/transport/SdlRouterService.java b/sdl_android/src/main/java/com/smartdevicelink/transport/SdlRouterService.java
index d3a4fd051..5ca8cec0e 100644
--- a/sdl_android/src/main/java/com/smartdevicelink/transport/SdlRouterService.java
+++ b/sdl_android/src/main/java/com/smartdevicelink/transport/SdlRouterService.java
@@ -1,6 +1,5 @@
package com.smartdevicelink.transport;
-import static com.smartdevicelink.proxy.constants.Names.info;
import static com.smartdevicelink.transport.TransportConstants.CONNECTED_DEVICE_STRING_EXTRA_NAME;
import static com.smartdevicelink.transport.TransportConstants.FORMED_PACKET_EXTRA_NAME;
import static com.smartdevicelink.transport.TransportConstants.HARDWARE_DISCONNECTED;
@@ -23,11 +22,14 @@ import org.json.JSONException;
import org.json.JSONObject;
import android.Manifest;
+
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
@@ -72,6 +74,30 @@ import com.smartdevicelink.transport.utl.ByteAraryMessageAssembler;
import com.smartdevicelink.transport.utl.ByteArrayMessageSpliter;
import com.smartdevicelink.util.AndroidTools;
import com.smartdevicelink.util.BitConverter;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.lang.ref.WeakReference;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+import java.util.Vector;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import static com.smartdevicelink.transport.TransportConstants.CONNECTED_DEVICE_STRING_EXTRA_NAME;
+import static com.smartdevicelink.transport.TransportConstants.FOREGROUND_EXTRA;
+import static com.smartdevicelink.transport.TransportConstants.FORMED_PACKET_EXTRA_NAME;
+import static com.smartdevicelink.transport.TransportConstants.HARDWARE_DISCONNECTED;
+import static com.smartdevicelink.transport.TransportConstants.SDL_NOTIFICATION_CHANNEL_ID;
+import static com.smartdevicelink.transport.TransportConstants.SDL_NOTIFICATION_CHANNEL_NAME;
+import static com.smartdevicelink.transport.TransportConstants.SEND_PACKET_TO_APP_LOCATION_EXTRA_NAME;
/**
* <b>This class should not be modified by anyone outside of the approved contributors of the SmartDeviceLink project.</b>
* This service is a central point of communication between hardware and the registered clients. It will multiplex a single transport
@@ -181,6 +207,7 @@ public class SdlRouterService extends Service{
registrationIntent.setAction(action);
registrationIntent.putExtra(TransportConstants.BIND_LOCATION_PACKAGE_NAME_EXTRA, this.getPackageName());
registrationIntent.putExtra(TransportConstants.BIND_LOCATION_CLASS_NAME_EXTRA, this.getClass().getName());
+ registrationIntent.setFlags((Intent.FLAG_RECEIVER_FOREGROUND));
return registrationIntent;
}
@@ -889,6 +916,9 @@ public class SdlRouterService extends Service{
}
}
if(intent != null ){
+ if(intent.getBooleanExtra(FOREGROUND_EXTRA, false)){
+ enterForeground();
+ }
if(intent.hasExtra(TransportConstants.PING_ROUTER_SERVICE_EXTRA)){
//Make sure we are listening on RFCOMM
if(startSequenceComplete){ //We only check if we are sure we are already through the start up process
@@ -1029,18 +1059,37 @@ public class SdlRouterService extends Service{
notification = builder.getNotification();
}else{
+ if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.O) {
+ //Now we need to add a notification channel
+ NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ String channelId = SDL_NOTIFICATION_CHANNEL_ID;
+ CharSequence channelName = SDL_NOTIFICATION_CHANNEL_NAME;
+ int importance = NotificationManager.IMPORTANCE_DEFAULT;
+ NotificationChannel notificationChannel = new NotificationChannel(channelId, channelName, importance);
+ notificationChannel.enableLights(false);
+ notificationChannel.enableVibration(false);
+ notificationManager.createNotificationChannel(notificationChannel);
+ builder.setChannelId(channelId);
+
+ }
notification = builder.build();
}
if(notification == null){
Log.e(TAG, "Notification was null");
+ return;
}
startForeground(FOREGROUND_SERVICE_ID, notification);
isForeground = true;
}
-
+
private void exitForeground(){
if(isForeground){
+ if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
+ NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ notificationManager.deleteNotificationChannel(TransportConstants.SDL_NOTIFICATION_CHANNEL_ID);
+ }
+
this.stopForeground(true);
}
}
@@ -1741,7 +1790,7 @@ public class SdlRouterService extends Service{
}
}
- @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
private boolean removeAllSessionsWithAppId(String appId){
synchronized(SESSION_LOCK){
if(sessionMap!=null){
diff --git a/sdl_android/src/main/java/com/smartdevicelink/transport/TransportBroker.java b/sdl_android/src/main/java/com/smartdevicelink/transport/TransportBroker.java
index 89187d524..1d1b3b792 100644
--- a/sdl_android/src/main/java/com/smartdevicelink/transport/TransportBroker.java
+++ b/sdl_android/src/main/java/com/smartdevicelink/transport/TransportBroker.java
@@ -7,6 +7,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -488,7 +489,7 @@ public class TransportBroker {
}
//Make sure we know where to bind to
if(this.routerService==null){
- if(!isRouterServiceRunning(getContext())){//We should be able to ignore this case because of the validation now
+ if((Build.VERSION.SDK_INT < Build.VERSION_CODES.O) && !isRouterServiceRunning(getContext())){//We should be able to ignore this case because of the validation now
Log.d(TAG,whereToReply + " found no router service. Shutting down.");
this.onHardwareDisconnected(null);
return false;
diff --git a/sdl_android/src/main/java/com/smartdevicelink/transport/TransportConstants.java b/sdl_android/src/main/java/com/smartdevicelink/transport/TransportConstants.java
index 6203aa3e7..8db44f500 100644
--- a/sdl_android/src/main/java/com/smartdevicelink/transport/TransportConstants.java
+++ b/sdl_android/src/main/java/com/smartdevicelink/transport/TransportConstants.java
@@ -10,6 +10,7 @@ package com.smartdevicelink.transport;
*/
public class TransportConstants {
public static final String START_ROUTER_SERVICE_ACTION ="sdl.router.startservice";
+ public static final String FOREGROUND_EXTRA = "foreground";
public static final String BIND_LOCATION_PACKAGE_NAME_EXTRA = "BIND_LOCATION_PACKAGE_NAME_EXTRA";
public static final String BIND_LOCATION_CLASS_NAME_EXTRA = "BIND_LOCATION_CLASS_NAME_EXTRA";
@@ -54,6 +55,12 @@ public class TransportConstants {
public static final String PING_ROUTER_SERVICE_EXTRA = "ping.router.service";
+ public static final String SDL_NOTIFICATION_CHANNEL_ID = "sdl_notification_channel";
+ public static final String SDL_NOTIFICATION_CHANNEL_NAME = "SmartDeviceLink";
+
+
+
+
/**
* This class houses all important router service versions
*/
diff --git a/sdl_android/src/main/java/com/smartdevicelink/util/AndroidTools.java b/sdl_android/src/main/java/com/smartdevicelink/util/AndroidTools.java
index ff8819ec3..bc55dcb07 100644
--- a/sdl_android/src/main/java/com/smartdevicelink/util/AndroidTools.java
+++ b/sdl_android/src/main/java/com/smartdevicelink/util/AndroidTools.java
@@ -2,9 +2,16 @@ package com.smartdevicelink.util;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.PackageManager;
-import android.content.pm.ServiceInfo;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+
+import com.smartdevicelink.transport.TransportConstants;
+
+import java.util.HashMap;
+import java.util.List;
public class AndroidTools {
/**
@@ -22,4 +29,24 @@ public class AndroidTools {
}
return false;
}
+ /**
+ * Get all SDL enabled apps. If the package name is null, it will return all apps. However, if the package name is included, the
+ * resulting hash map will not include the app with that package name.
+ * @param context
+ * @param myPackageName
+ * @return
+ */
+ public static HashMap<String,ResolveInfo> getSdlEnabledApps(Context context, String myPackageName){
+ Intent intent = new Intent(TransportConstants.START_ROUTER_SERVICE_ACTION);
+ List<ResolveInfo> infos = context.getPackageManager().queryBroadcastReceivers(intent, 0);
+ HashMap<String,ResolveInfo> sdlMultiList = new HashMap<String,ResolveInfo>();
+ for(ResolveInfo info: infos){
+ if(info.activityInfo.applicationInfo.packageName.equals(myPackageName)){
+ continue; //Ignoring my own package
+ }
+ sdlMultiList.put(info.activityInfo.packageName, info);
+ }
+ return sdlMultiList;
+ }
+
}
diff --git a/sdl_android/src/main/java/com/smartdevicelink/util/ServiceFinder.java b/sdl_android/src/main/java/com/smartdevicelink/util/ServiceFinder.java
new file mode 100644
index 000000000..c115d046c
--- /dev/null
+++ b/sdl_android/src/main/java/com/smartdevicelink/util/ServiceFinder.java
@@ -0,0 +1,141 @@
+package com.smartdevicelink.util;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ResolveInfo;
+import android.os.Handler;
+import android.util.Log;
+
+import com.smartdevicelink.transport.SdlRouterService;
+
+import java.util.HashMap;
+import java.util.Vector;
+
+import static com.smartdevicelink.transport.TransportConstants.BIND_LOCATION_CLASS_NAME_EXTRA;
+import static com.smartdevicelink.transport.TransportConstants.BIND_LOCATION_PACKAGE_NAME_EXTRA;
+import static com.smartdevicelink.transport.TransportConstants.SEND_PACKET_TO_APP_LOCATION_EXTRA_NAME;
+
+/**
+ * Created by Joey Grover on 8/18/17.
+ */
+
+public class ServiceFinder {
+ public static final String TAG = ServiceFinder.class.getSimpleName();
+
+ private static final int TIMEOUT = 1000;
+ final String receiverLocation;
+ final Context context;
+ final ServiceFinderCallback callback;
+ final Vector<ComponentName> services;
+ final HashMap<String, ResolveInfo> sdlMultiMap;
+ final Handler timeoutHandler;
+ final Runnable timeoutRunnable;
+
+
+ public ServiceFinder(Context context, String packageName, final ServiceFinderCallback callback) {
+ this.receiverLocation = packageName + ".ServiceFinder";
+ this.context = context.getApplicationContext();
+ this.callback = callback;
+ this.services = new Vector<>();
+
+ this.sdlMultiMap = AndroidTools.getSdlEnabledApps(context, packageName);
+
+ this.context.registerReceiver(mainServiceReceiver, new IntentFilter(this.receiverLocation));
+
+ timeoutRunnable = new Runnable() {
+ @Override
+ public void run() {
+ onFinished();
+ }
+ };
+ timeoutHandler = new Handler();
+ timeoutHandler.postDelayed(timeoutRunnable, TIMEOUT + (50 * packageName.length()));
+
+ //Send out our broadcast
+ context.sendBroadcast(createQueryIntent(this.receiverLocation));
+
+
+ }
+
+ BroadcastReceiver mainServiceReceiver = new BroadcastReceiver() {
+ private final Object LIST_LOCK = new Object();
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.d(TAG, "Received intent " + intent);
+ if (intent != null) {
+ String packageName = intent.getStringExtra(BIND_LOCATION_PACKAGE_NAME_EXTRA);
+ String className = intent.getStringExtra(BIND_LOCATION_CLASS_NAME_EXTRA);
+ Log.d(TAG, "Received intent from package: " + packageName + ". Classname: " + className);
+ synchronized (LIST_LOCK) {
+ //Add to running services
+ services.add(new ComponentName(packageName, className));
+ //Remove from our waiting for response list
+ sdlMultiMap.remove(packageName);
+
+ //If list is empty, return to callback and unregister
+ if (sdlMultiMap.isEmpty() && callback != null) {
+ timeoutHandler.removeCallbacks(timeoutRunnable);
+ onFinished();
+ }
+ }
+ }
+ }
+ };
+
+ private void onFinished() {
+ if (callback != null) {
+ callback.onComplete(services);
+ }
+ context.unregisterReceiver(mainServiceReceiver);
+
+ }
+
+// /**
+// * Get all SDL enabled apps. If the package name is null, it will return all apps. However, if the package name is included, the
+// * resulting hash map will not include the app with that package name.
+// *
+// * @param context
+// * @param packageName
+// * @return
+// */
+// public static HashMap<String, ResolveInfo> getSdlEnabledApps(Context context, String packageName) {
+// Intent intent = new Intent(TransportConstants.START_ROUTER_SERVICE_ACTION);
+// PackageManager manager = context.getPackageManager();
+// List<ResolveInfo> infos = manager.queryBroadcastReceivers(intent, 0);
+// HashMap<String, ResolveInfo> sdlMultiMap = new HashMap<String, ResolveInfo>();
+// for (ResolveInfo info : infos) {
+// //Log.d(TAG, "Sdl enabled app: " + info.activityInfo.packageName);
+// if (info.activityInfo.applicationInfo.packageName.equals(packageName)) {
+// //Log.d(TAG, "Ignoring my own package");
+// continue;
+// }
+//
+// sdlMultiMap.put(info.activityInfo.packageName, info);
+// try {
+// ServiceInfo[] services = manager.getPackageInfo(info.activityInfo.applicationInfo.packageName, PackageManager.GET_SERVICES).services;
+// for (int i = 0; i < services.length; i++) {
+// Log.d(TAG, "Found : " + services[i].name);
+// }
+// } catch (PackageManager.NameNotFoundException e) {
+// e.printStackTrace();
+// }
+// }
+// return sdlMultiMap;
+// }
+
+ private static Intent createQueryIntent(String receiverLocation) {
+ Intent intent = new Intent();
+ intent.setAction(SdlRouterService.REGISTER_WITH_ROUTER_ACTION);
+ intent.putExtra(SEND_PACKET_TO_APP_LOCATION_EXTRA_NAME, receiverLocation);
+ intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ return intent;
+ }
+
+ public interface ServiceFinderCallback {
+ void onComplete(Vector<ComponentName> routerServices);
+ }
+}