2014-11-16 23:14:06 -08:00
|
|
|
/*
|
|
|
|
* Copyright 2014 Albert Vaca Cintora <albertvaka@gmail.com>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License as
|
|
|
|
* published by the Free Software Foundation; either version 2 of
|
|
|
|
* the License or (at your option) version 3 or any later version
|
|
|
|
* accepted by the membership of KDE e.V. (or its successor approved
|
|
|
|
* by the membership of KDE e.V.), which shall act as a proxy
|
|
|
|
* defined in Section 14 of version 3 of the license.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2013-09-05 01:37:59 +02:00
|
|
|
package org.kde.kdeconnect;
|
2013-06-06 05:57:06 +02:00
|
|
|
|
|
|
|
import android.app.Service;
|
2013-06-18 20:04:53 +02:00
|
|
|
import android.content.Context;
|
2013-06-06 05:57:06 +02:00
|
|
|
import android.content.Intent;
|
2013-08-10 05:16:59 +02:00
|
|
|
import android.content.IntentFilter;
|
2013-06-06 05:57:06 +02:00
|
|
|
import android.content.SharedPreferences;
|
2013-06-18 02:14:22 +02:00
|
|
|
import android.os.Binder;
|
2013-06-06 05:57:06 +02:00
|
|
|
import android.os.IBinder;
|
2013-07-31 18:02:22 +02:00
|
|
|
import android.preference.PreferenceManager;
|
2013-06-06 05:57:06 +02:00
|
|
|
import android.util.Log;
|
|
|
|
|
2013-09-16 17:36:26 +02:00
|
|
|
import org.kde.kdeconnect.Backends.BaseLink;
|
2013-09-16 15:24:21 +02:00
|
|
|
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
|
|
|
import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider;
|
2015-06-20 04:09:02 +05:30
|
|
|
import org.kde.kdeconnect.Helpers.SecurityHelpers.RsaHelper;
|
|
|
|
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
|
2013-06-06 05:57:06 +02:00
|
|
|
|
|
|
|
import java.util.ArrayList;
|
2015-09-09 12:34:42 -07:00
|
|
|
import java.util.HashSet;
|
2013-08-16 10:31:01 +02:00
|
|
|
import java.util.Set;
|
2015-09-04 06:04:22 -07:00
|
|
|
import java.util.concurrent.ConcurrentHashMap;
|
2013-08-21 21:30:25 +02:00
|
|
|
import java.util.concurrent.locks.Lock;
|
|
|
|
import java.util.concurrent.locks.ReentrantLock;
|
2013-06-06 05:57:06 +02:00
|
|
|
|
2013-06-19 16:15:25 +02:00
|
|
|
public class BackgroundService extends Service {
|
2013-06-06 05:57:06 +02:00
|
|
|
|
2015-09-04 06:04:22 -07:00
|
|
|
public interface DeviceListChangedCallback {
|
|
|
|
void onDeviceListChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
private final ConcurrentHashMap<String, DeviceListChangedCallback> deviceListChangedCallbacks = new ConcurrentHashMap<>();
|
2013-06-18 02:14:22 +02:00
|
|
|
|
2015-08-10 00:26:58 -07:00
|
|
|
private final ArrayList<BaseLinkProvider> linkProviders = new ArrayList<>();
|
2013-06-18 02:14:22 +02:00
|
|
|
|
2015-09-04 06:04:22 -07:00
|
|
|
private final ConcurrentHashMap<String, Device> devices = new ConcurrentHashMap<>();
|
2013-06-06 05:57:06 +02:00
|
|
|
|
2015-09-09 12:34:42 -07:00
|
|
|
private final HashSet<Object> discoveryModeAcquisitions = new HashSet<>();
|
2015-09-09 00:45:56 +02:00
|
|
|
|
2015-09-10 02:35:33 -07:00
|
|
|
public boolean acquireDiscoveryMode(Object key) {
|
2015-09-09 12:34:42 -07:00
|
|
|
boolean wasEmpty = discoveryModeAcquisitions.isEmpty();
|
|
|
|
discoveryModeAcquisitions.add(key);
|
|
|
|
if (wasEmpty) {
|
2015-09-09 00:45:56 +02:00
|
|
|
onNetworkChange();
|
2015-09-09 12:34:42 -07:00
|
|
|
}
|
2016-06-17 10:00:05 +02:00
|
|
|
//Log.e("acquireDiscoveryMode",key.getClass().getName() +" ["+discoveryModeAcquisitions.size()+"]");
|
2015-09-10 02:35:33 -07:00
|
|
|
return wasEmpty;
|
2015-09-09 12:34:42 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
public void releaseDiscoveryMode(Object key) {
|
|
|
|
boolean removed = discoveryModeAcquisitions.remove(key);
|
2016-06-17 10:00:05 +02:00
|
|
|
//Log.e("releaseDiscoveryMode",key.getClass().getName() +" ["+discoveryModeAcquisitions.size()+"]");
|
2015-09-09 12:34:42 -07:00
|
|
|
if (removed && discoveryModeAcquisitions.isEmpty()) {
|
2015-09-09 00:45:56 +02:00
|
|
|
cleanDevices();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-12 13:21:06 -07:00
|
|
|
public static void addGuiInUseCounter(Context activity) {
|
2015-09-10 02:35:33 -07:00
|
|
|
addGuiInUseCounter(activity, false);
|
|
|
|
}
|
|
|
|
|
2015-09-12 13:21:06 -07:00
|
|
|
public static void addGuiInUseCounter(final Context activity, final boolean forceNetworkRefresh) {
|
2015-09-09 12:34:42 -07:00
|
|
|
BackgroundService.RunCommand(activity, new BackgroundService.InstanceCallback() {
|
|
|
|
@Override
|
|
|
|
public void onServiceStart(BackgroundService service) {
|
2015-09-10 02:35:33 -07:00
|
|
|
boolean refreshed = service.acquireDiscoveryMode(activity);
|
|
|
|
if (!refreshed && forceNetworkRefresh) {
|
|
|
|
service.onNetworkChange();
|
|
|
|
}
|
2015-09-09 12:34:42 -07:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-09-12 13:21:06 -07:00
|
|
|
public static void removeGuiInUseCounter(final Context activity) {
|
2015-09-09 12:34:42 -07:00
|
|
|
BackgroundService.RunCommand(activity, new BackgroundService.InstanceCallback() {
|
|
|
|
@Override
|
|
|
|
public void onServiceStart(BackgroundService service) {
|
|
|
|
//If no user interface is open, close the connections open to other devices
|
|
|
|
service.releaseDiscoveryMode(activity);
|
|
|
|
}
|
|
|
|
});
|
2015-09-09 00:45:56 +02:00
|
|
|
}
|
2013-06-06 05:57:06 +02:00
|
|
|
|
2014-03-29 01:47:15 +01:00
|
|
|
private final Device.PairingCallback devicePairingCallback = new Device.PairingCallback() {
|
2013-09-03 17:58:59 +02:00
|
|
|
@Override
|
|
|
|
public void incomingRequest() {
|
2015-09-04 06:04:22 -07:00
|
|
|
onDeviceListChanged();
|
2013-09-03 17:58:59 +02:00
|
|
|
}
|
|
|
|
@Override
|
|
|
|
public void pairingSuccessful() {
|
2015-09-04 06:04:22 -07:00
|
|
|
onDeviceListChanged();
|
2013-09-03 17:58:59 +02:00
|
|
|
}
|
|
|
|
@Override
|
|
|
|
public void pairingFailed(String error) {
|
2015-09-04 06:04:22 -07:00
|
|
|
onDeviceListChanged();
|
2013-09-03 17:58:59 +02:00
|
|
|
}
|
|
|
|
@Override
|
|
|
|
public void unpaired() {
|
2015-09-04 06:04:22 -07:00
|
|
|
onDeviceListChanged();
|
2013-09-03 17:58:59 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-09-04 06:04:22 -07:00
|
|
|
private void onDeviceListChanged() {
|
|
|
|
for(DeviceListChangedCallback callback : deviceListChangedCallbacks.values()) {
|
|
|
|
callback.onDeviceListChanged();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-16 10:31:01 +02:00
|
|
|
private void loadRememberedDevicesFromSettings() {
|
2013-11-06 21:13:37 +01:00
|
|
|
//Log.e("BackgroundService", "Loading remembered trusted devices");
|
2013-08-20 00:37:08 +02:00
|
|
|
SharedPreferences preferences = getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
|
|
|
|
Set<String> trustedDevices = preferences.getAll().keySet();
|
2013-08-16 10:31:01 +02:00
|
|
|
for(String deviceId : trustedDevices) {
|
2013-11-06 21:13:37 +01:00
|
|
|
//Log.e("BackgroundService", "Loading device "+deviceId);
|
2013-08-20 00:37:08 +02:00
|
|
|
if (preferences.getBoolean(deviceId, false)) {
|
2013-10-01 03:24:42 +02:00
|
|
|
Device device = new Device(this, deviceId);
|
2013-09-03 17:58:59 +02:00
|
|
|
devices.put(deviceId,device);
|
|
|
|
device.addPairingCallback(devicePairingCallback);
|
2013-08-20 00:37:08 +02:00
|
|
|
}
|
2013-08-16 10:31:01 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-29 01:47:15 +01:00
|
|
|
private void registerLinkProviders() {
|
2013-07-31 18:02:22 +02:00
|
|
|
|
|
|
|
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
|
|
|
|
|
2013-09-10 11:56:57 +02:00
|
|
|
//if (settings.getBoolean("loopback_link", true)) {
|
|
|
|
// linkProviders.add(new LoopbackLinkProvider(this));
|
|
|
|
//}
|
2013-09-05 17:03:59 +02:00
|
|
|
|
|
|
|
if (settings.getBoolean("lan_link", true)) {
|
2013-09-05 00:21:39 +02:00
|
|
|
linkProviders.add(new LanLinkProvider(this));
|
2013-07-31 18:02:22 +02:00
|
|
|
}
|
2013-08-01 02:30:58 +02:00
|
|
|
|
2013-07-29 18:42:12 +02:00
|
|
|
}
|
|
|
|
|
2015-06-25 04:20:03 +05:30
|
|
|
public ArrayList<BaseLinkProvider> getLinkProviders() {
|
|
|
|
return linkProviders;
|
|
|
|
}
|
|
|
|
|
2013-07-31 18:02:22 +02:00
|
|
|
public Device getDevice(String id) {
|
|
|
|
return devices.get(id);
|
2013-07-23 16:11:54 +02:00
|
|
|
}
|
|
|
|
|
2015-09-09 00:45:56 +02:00
|
|
|
private void cleanDevices() {
|
2016-06-16 23:16:18 +02:00
|
|
|
new Thread(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
for(Device d : devices.values()) {
|
|
|
|
if (!d.isPaired() && !d.isPairRequested() && !d.isPairRequestedByPeer() && !d.deviceShouldBeKeptAlive()) {
|
|
|
|
d.disconnect();
|
|
|
|
}
|
|
|
|
}
|
2015-09-09 02:03:36 -07:00
|
|
|
}
|
2016-06-17 10:00:05 +02:00
|
|
|
}).start();
|
2015-09-09 00:45:56 +02:00
|
|
|
}
|
|
|
|
|
2014-03-29 01:47:15 +01:00
|
|
|
private final BaseLinkProvider.ConnectionReceiver deviceListener = new BaseLinkProvider.ConnectionReceiver() {
|
2013-07-31 18:02:22 +02:00
|
|
|
@Override
|
2013-09-16 17:36:26 +02:00
|
|
|
public void onConnectionReceived(final NetworkPackage identityPackage, final BaseLink link) {
|
2013-08-16 10:31:01 +02:00
|
|
|
|
2013-08-19 23:59:13 +02:00
|
|
|
String deviceId = identityPackage.getString("deviceId");
|
|
|
|
|
|
|
|
Device device = devices.get(deviceId);
|
2013-07-31 18:02:22 +02:00
|
|
|
|
2013-08-19 23:59:13 +02:00
|
|
|
if (device != null) {
|
2015-04-04 11:37:12 -07:00
|
|
|
Log.i("KDE/BackgroundService", "addLink, known device: " + deviceId);
|
2013-10-01 03:24:42 +02:00
|
|
|
device.addLink(identityPackage, link);
|
2013-08-19 23:59:13 +02:00
|
|
|
} else {
|
2015-04-04 11:37:12 -07:00
|
|
|
Log.i("KDE/BackgroundService", "addLink,unknown device: " + deviceId);
|
2013-10-01 03:24:42 +02:00
|
|
|
device = new Device(BackgroundService.this, identityPackage, link);
|
2016-01-10 03:16:14 -08:00
|
|
|
if (device.isPaired() || device.isPairRequested() || device.isPairRequestedByPeer()
|
2016-03-02 11:39:49 -08:00
|
|
|
|| link.linkShouldBeKeptAlive()
|
2015-09-12 13:21:06 -07:00
|
|
|
||!discoveryModeAcquisitions.isEmpty() )
|
|
|
|
{
|
2015-09-09 00:45:56 +02:00
|
|
|
devices.put(deviceId, device);
|
|
|
|
device.addPairingCallback(devicePairingCallback);
|
2015-09-12 13:21:06 -07:00
|
|
|
} else {
|
|
|
|
device.disconnect();
|
2015-09-09 00:45:56 +02:00
|
|
|
}
|
2013-08-19 23:59:13 +02:00
|
|
|
}
|
2013-09-03 17:58:59 +02:00
|
|
|
|
2015-09-04 06:04:22 -07:00
|
|
|
onDeviceListChanged();
|
2013-07-31 18:02:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2013-09-16 17:36:26 +02:00
|
|
|
public void onConnectionLost(BaseLink link) {
|
2013-07-31 18:02:22 +02:00
|
|
|
Device d = devices.get(link.getDeviceId());
|
2015-04-04 11:37:12 -07:00
|
|
|
Log.i("KDE/onConnectionLost", "removeLink, deviceId: " + link.getDeviceId());
|
2013-07-31 18:02:22 +02:00
|
|
|
if (d != null) {
|
|
|
|
d.removeLink(link);
|
2015-09-12 13:21:06 -07:00
|
|
|
if (!d.isReachable() && !d.isPaired()) {
|
2013-11-06 21:13:37 +01:00
|
|
|
//Log.e("onConnectionLost","Removing connection device because it was not paired");
|
2013-08-16 10:31:01 +02:00
|
|
|
devices.remove(link.getDeviceId());
|
2013-09-03 17:58:59 +02:00
|
|
|
d.removePairingCallback(devicePairingCallback);
|
2013-08-16 10:31:01 +02:00
|
|
|
}
|
2013-08-19 23:59:13 +02:00
|
|
|
} else {
|
2015-09-09 03:22:34 -07:00
|
|
|
//Log.d("KDE/onConnectionLost","Removing connection to unknown device");
|
2013-07-31 18:02:22 +02:00
|
|
|
}
|
2015-09-04 06:04:22 -07:00
|
|
|
onDeviceListChanged();
|
2013-07-31 18:02:22 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-09-04 06:04:22 -07:00
|
|
|
public ConcurrentHashMap<String, Device> getDevices() {
|
2013-08-16 10:31:01 +02:00
|
|
|
return devices;
|
2013-06-18 03:05:32 +02:00
|
|
|
}
|
|
|
|
|
2013-08-07 10:44:52 +02:00
|
|
|
public void onNetworkChange() {
|
|
|
|
for (BaseLinkProvider a : linkProviders) {
|
|
|
|
a.onNetworkChange();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-31 19:10:51 +02:00
|
|
|
public void addConnectionListener(BaseLinkProvider.ConnectionReceiver cr) {
|
|
|
|
for (BaseLinkProvider a : linkProviders) {
|
|
|
|
a.addConnectionReceiver(cr);
|
2013-06-17 12:23:08 +02:00
|
|
|
}
|
2013-06-06 05:57:06 +02:00
|
|
|
}
|
|
|
|
|
2013-08-20 09:58:25 +02:00
|
|
|
public void removeConnectionListener(BaseLinkProvider.ConnectionReceiver cr) {
|
|
|
|
for (BaseLinkProvider a : linkProviders) {
|
|
|
|
a.removeConnectionReceiver(cr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-04 06:04:22 -07:00
|
|
|
public void addDeviceListChangedCallback(String key, DeviceListChangedCallback callback) {
|
|
|
|
deviceListChangedCallbacks.put(key, callback);
|
2013-09-03 17:58:59 +02:00
|
|
|
}
|
2015-09-04 06:04:22 -07:00
|
|
|
public void removeDeviceListChangedCallback(String key) {
|
|
|
|
deviceListChangedCallbacks.remove(key);
|
2013-09-03 17:58:59 +02:00
|
|
|
}
|
|
|
|
|
2013-07-04 19:17:22 +02:00
|
|
|
//This will called only once, even if we launch the service intent several times
|
2013-06-06 05:57:06 +02:00
|
|
|
@Override
|
|
|
|
public void onCreate() {
|
2013-07-02 15:22:05 +02:00
|
|
|
super.onCreate();
|
2015-06-28 20:40:45 +05:30
|
|
|
|
2013-08-10 05:16:59 +02:00
|
|
|
// Register screen on listener
|
|
|
|
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
|
2013-08-16 10:31:01 +02:00
|
|
|
registerReceiver(new KdeConnectBroadcastReceiver(), filter);
|
2013-08-10 05:16:59 +02:00
|
|
|
|
2015-09-09 00:45:56 +02:00
|
|
|
Log.i("KDE/BackgroundService", "Service not started yet, initializing...");
|
2013-06-17 12:23:08 +02:00
|
|
|
|
2015-06-20 04:09:02 +05:30
|
|
|
initializeSecurityParameters();
|
2013-08-16 10:31:01 +02:00
|
|
|
loadRememberedDevicesFromSettings();
|
2013-07-24 18:42:33 +02:00
|
|
|
registerLinkProviders();
|
2013-07-31 19:10:51 +02:00
|
|
|
|
|
|
|
//Link Providers need to be already registered
|
|
|
|
addConnectionListener(deviceListener);
|
2013-07-23 16:11:54 +02:00
|
|
|
|
2015-09-09 00:45:56 +02:00
|
|
|
for (BaseLinkProvider a : linkProviders) {
|
|
|
|
a.onStart();
|
|
|
|
}
|
2013-07-23 16:11:54 +02:00
|
|
|
}
|
2013-06-17 12:23:08 +02:00
|
|
|
|
2015-06-20 04:09:02 +05:30
|
|
|
void initializeSecurityParameters() {
|
|
|
|
RsaHelper.initialiseRsaKeys(this);
|
|
|
|
SslHelper.initialiseCertificate(this);
|
2013-09-03 17:58:59 +02:00
|
|
|
}
|
|
|
|
|
2013-06-06 05:57:06 +02:00
|
|
|
@Override
|
|
|
|
public void onDestroy() {
|
2015-09-09 00:45:56 +02:00
|
|
|
for (BaseLinkProvider a : linkProviders) {
|
|
|
|
a.onStop();
|
|
|
|
}
|
2013-06-06 05:57:06 +02:00
|
|
|
super.onDestroy();
|
|
|
|
}
|
|
|
|
|
2013-07-04 19:17:22 +02:00
|
|
|
@Override
|
|
|
|
public IBinder onBind (Intent intent) {
|
|
|
|
return new Binder();
|
|
|
|
}
|
2013-06-18 02:14:22 +02:00
|
|
|
|
2013-06-18 20:04:53 +02:00
|
|
|
|
2013-07-04 19:17:22 +02:00
|
|
|
//To use the service from the gui
|
2013-06-18 20:04:53 +02:00
|
|
|
|
2013-07-02 15:22:05 +02:00
|
|
|
public interface InstanceCallback {
|
2013-06-18 20:04:53 +02:00
|
|
|
void onServiceStart(BackgroundService service);
|
|
|
|
}
|
|
|
|
|
2015-08-10 00:26:58 -07:00
|
|
|
private final static ArrayList<InstanceCallback> callbacks = new ArrayList<>();
|
2013-07-23 16:11:54 +02:00
|
|
|
|
2014-03-29 01:47:15 +01:00
|
|
|
private final static Lock mutex = new ReentrantLock(true);
|
2013-08-21 21:30:25 +02:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
|
|
|
//This will be called for each intent launch, even if the service is already started and it is reused
|
|
|
|
mutex.lock();
|
2015-10-04 00:23:50 +02:00
|
|
|
try {
|
|
|
|
for (InstanceCallback c : callbacks) {
|
|
|
|
c.onServiceStart(this);
|
|
|
|
}
|
|
|
|
callbacks.clear();
|
|
|
|
} finally {
|
|
|
|
mutex.unlock();
|
2013-08-21 21:30:25 +02:00
|
|
|
}
|
|
|
|
return Service.START_STICKY;
|
|
|
|
}
|
|
|
|
|
2013-06-18 20:04:53 +02:00
|
|
|
public static void Start(Context c) {
|
2013-07-02 15:22:05 +02:00
|
|
|
RunCommand(c, null);
|
2013-06-18 20:04:53 +02:00
|
|
|
}
|
2013-07-23 16:11:54 +02:00
|
|
|
|
2015-06-05 20:35:14 -07:00
|
|
|
public static void RunCommand(final Context c, final InstanceCallback callback) {
|
|
|
|
new Thread(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
if (callback != null) {
|
|
|
|
mutex.lock();
|
2015-10-04 00:23:50 +02:00
|
|
|
try {
|
|
|
|
callbacks.add(callback);
|
|
|
|
} finally {
|
|
|
|
mutex.unlock();
|
|
|
|
}
|
2015-06-05 20:35:14 -07:00
|
|
|
}
|
|
|
|
Intent serviceIntent = new Intent(c, BackgroundService.class);
|
|
|
|
c.startService(serviceIntent);
|
|
|
|
}
|
|
|
|
}).start();
|
2013-07-02 15:22:05 +02:00
|
|
|
}
|
2013-06-18 20:04:53 +02:00
|
|
|
|
2013-06-06 05:57:06 +02:00
|
|
|
}
|