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-09-03 17:58:59 +02:00
|
|
|
import android.util.Base64;
|
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;
|
2013-10-30 01:36:00 +01:00
|
|
|
import org.kde.kdeconnect.UserInterface.MainSettingsActivity;
|
2013-06-06 05:57:06 +02:00
|
|
|
|
2013-09-03 17:58:59 +02:00
|
|
|
import java.security.KeyPair;
|
|
|
|
import java.security.KeyPairGenerator;
|
2013-06-06 05:57:06 +02:00
|
|
|
import java.util.ArrayList;
|
2013-07-23 16:11:54 +02:00
|
|
|
import java.util.HashMap;
|
2013-08-16 10:31:01 +02:00
|
|
|
import java.util.Set;
|
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
|
|
|
|
2014-03-29 01:47:15 +01:00
|
|
|
private final ArrayList<BaseLinkProvider> linkProviders = new ArrayList<BaseLinkProvider>();
|
2013-06-18 02:14:22 +02:00
|
|
|
|
2014-03-29 01:47:15 +01:00
|
|
|
private final HashMap<String, Device> devices = new HashMap<String, Device>();
|
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() {
|
|
|
|
if (deviceListChangedCallback != null) deviceListChangedCallback.onDeviceListChanged();
|
|
|
|
}
|
|
|
|
@Override
|
|
|
|
public void pairingSuccessful() {
|
|
|
|
if (deviceListChangedCallback != null) deviceListChangedCallback.onDeviceListChanged();
|
|
|
|
}
|
|
|
|
@Override
|
|
|
|
public void pairingFailed(String error) {
|
|
|
|
if (deviceListChangedCallback != null) deviceListChangedCallback.onDeviceListChanged();
|
|
|
|
}
|
|
|
|
@Override
|
|
|
|
public void unpaired() {
|
|
|
|
if (deviceListChangedCallback != null) deviceListChangedCallback.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
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2015-04-04 11:37:12 -07:00
|
|
|
Log.i("KDE/BackgroundService", "Connection accepted!");
|
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);
|
2013-08-19 23:59:13 +02:00
|
|
|
devices.put(deviceId, device);
|
2013-09-03 17:58:59 +02:00
|
|
|
device.addPairingCallback(devicePairingCallback);
|
2013-08-19 23:59:13 +02:00
|
|
|
}
|
2013-09-03 17:58:59 +02:00
|
|
|
|
|
|
|
if (deviceListChangedCallback != null) deviceListChangedCallback.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);
|
2013-09-03 17:58:59 +02: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-04-04 11:37:12 -07:00
|
|
|
Log.e("KDE/onConnectionLost","Removing connection to unknown device, this should not happen");
|
2013-07-31 18:02:22 +02:00
|
|
|
}
|
2013-09-03 17:58:59 +02:00
|
|
|
if (deviceListChangedCallback != null) deviceListChangedCallback.onDeviceListChanged();
|
2013-07-31 18:02:22 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-08-16 10:31:01 +02:00
|
|
|
public HashMap<String, Device> getDevices() {
|
|
|
|
return devices;
|
2013-06-18 03:05:32 +02:00
|
|
|
}
|
|
|
|
|
2013-07-31 19:10:51 +02:00
|
|
|
public void startDiscovery() {
|
2015-04-04 11:37:12 -07:00
|
|
|
Log.i("KDE/BackgroundService","StartDiscovery");
|
2013-07-29 18:42:12 +02:00
|
|
|
for (BaseLinkProvider a : linkProviders) {
|
2013-07-31 19:10:51 +02:00
|
|
|
a.onStart();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void stopDiscovery() {
|
2015-04-04 11:37:12 -07:00
|
|
|
Log.i("KDE/BackgroundService","StopDiscovery");
|
2013-07-31 19:10:51 +02:00
|
|
|
for (BaseLinkProvider a : linkProviders) {
|
|
|
|
a.onStop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-07 10:44:52 +02:00
|
|
|
public void onNetworkChange() {
|
2015-04-04 11:37:12 -07:00
|
|
|
Log.i("KDE/BackgroundService","OnNetworkChange");
|
2013-08-07 10:44:52 +02:00
|
|
|
for (BaseLinkProvider a : linkProviders) {
|
|
|
|
a.onNetworkChange();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-31 19:10:51 +02:00
|
|
|
public void addConnectionListener(BaseLinkProvider.ConnectionReceiver cr) {
|
2015-04-04 11:37:12 -07:00
|
|
|
Log.i("KDE/BackgroundService","Registering connection listener");
|
2013-07-31 19:10:51 +02:00
|
|
|
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) {
|
2015-04-04 11:37:12 -07:00
|
|
|
Log.i("KDE/BackgroundService","Removing connection listener");
|
2013-08-20 09:58:25 +02:00
|
|
|
for (BaseLinkProvider a : linkProviders) {
|
|
|
|
a.removeConnectionReceiver(cr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-03 17:58:59 +02:00
|
|
|
public interface DeviceListChangedCallback {
|
|
|
|
void onDeviceListChanged();
|
|
|
|
}
|
|
|
|
private DeviceListChangedCallback deviceListChangedCallback = null;
|
|
|
|
public void setDeviceListChangedCallback(DeviceListChangedCallback callback) {
|
|
|
|
this.deviceListChangedCallback = callback;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
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-04-04 11:37:12 -07:00
|
|
|
Log.i("KDE/BackgroundService","Service not started yet, initializing...");
|
2013-06-17 12:23:08 +02:00
|
|
|
|
2013-09-03 17:58:59 +02:00
|
|
|
initializeRsaKeys();
|
2013-10-30 01:36:00 +01:00
|
|
|
MainSettingsActivity.initializeDeviceName(this);
|
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
|
|
|
startDiscovery();
|
|
|
|
|
|
|
|
}
|
2013-06-17 12:23:08 +02:00
|
|
|
|
2013-09-03 17:58:59 +02:00
|
|
|
private void initializeRsaKeys() {
|
|
|
|
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
|
|
|
|
|
|
|
|
if (!settings.contains("publicKey") || !settings.contains("privateKey")) {
|
|
|
|
|
|
|
|
KeyPair keyPair;
|
|
|
|
try {
|
|
|
|
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
|
|
|
|
keyGen.initialize(2048);
|
|
|
|
keyPair = keyGen.genKeyPair();
|
|
|
|
} catch(Exception e) {
|
|
|
|
e.printStackTrace();
|
2015-04-04 11:37:12 -07:00
|
|
|
Log.e("KDE/initializeRsaKeys","Exception");
|
2013-09-03 17:58:59 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
byte[] publicKey = keyPair.getPublic().getEncoded();
|
|
|
|
byte[] privateKey = keyPair.getPrivate().getEncoded();
|
|
|
|
|
|
|
|
SharedPreferences.Editor edit = settings.edit();
|
|
|
|
edit.putString("publicKey",Base64.encodeToString(publicKey, 0).trim()+"\n");
|
|
|
|
edit.putString("privateKey",Base64.encodeToString(privateKey, 0));
|
2015-01-31 00:16:06 -08:00
|
|
|
edit.apply();
|
2013-09-03 17:58:59 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
// Encryption and decryption test
|
|
|
|
//================================
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
NetworkPackage np = NetworkPackage.createIdentityPackage(this);
|
|
|
|
|
|
|
|
SharedPreferences globalSettings = PreferenceManager.getDefaultSharedPreferences(this);
|
|
|
|
|
|
|
|
byte[] publicKeyBytes = Base64.decode(globalSettings.getString("publicKey",""), 0);
|
|
|
|
PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(publicKeyBytes));
|
|
|
|
|
|
|
|
np.encrypt(publicKey);
|
|
|
|
|
|
|
|
byte[] privateKeyBytes = Base64.decode(globalSettings.getString("privateKey",""), 0);
|
|
|
|
PrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));
|
|
|
|
|
|
|
|
NetworkPackage decrypted = np.decrypt(privateKey);
|
|
|
|
Log.e("ENCRYPTION AND DECRYPTION TEST", decrypted.serialize());
|
|
|
|
} catch (Exception e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
Log.e("ENCRYPTION AND DECRYPTION TEST","Exception: "+e);
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-06-06 05:57:06 +02:00
|
|
|
@Override
|
|
|
|
public void onDestroy() {
|
2015-04-04 11:37:12 -07:00
|
|
|
Log.i("KDE/BackgroundService", "Destroying");
|
2013-08-08 04:12:28 +02:00
|
|
|
stopDiscovery();
|
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);
|
|
|
|
}
|
|
|
|
|
2014-03-29 01:47:15 +01:00
|
|
|
private final static ArrayList<InstanceCallback> callbacks = new ArrayList<InstanceCallback>();
|
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();
|
|
|
|
for (InstanceCallback c : callbacks) {
|
|
|
|
c.onServiceStart(this);
|
|
|
|
}
|
|
|
|
callbacks.clear();
|
|
|
|
mutex.unlock();
|
|
|
|
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();
|
|
|
|
callbacks.add(callback);
|
|
|
|
mutex.unlock();
|
|
|
|
}
|
|
|
|
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
|
|
|
}
|