File indexing completed on 2024-12-22 04:41:42

0001 /*
0002  * SPDX-FileCopyrightText: 2023 Albert Vaca Cintora <albertvaka@gmail.com>
0003  *
0004  * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005  */
0006 
0007 package org.kde.kdeconnect;
0008 
0009 import android.Manifest;
0010 import android.app.Application;
0011 import android.content.Context;
0012 import android.content.SharedPreferences;
0013 import android.content.pm.PackageManager;
0014 import android.os.Build;
0015 import android.util.Log;
0016 
0017 import androidx.annotation.NonNull;
0018 import androidx.core.app.ActivityCompat;
0019 import androidx.core.content.ContextCompat;
0020 
0021 import org.kde.kdeconnect.Backends.BaseLink;
0022 import org.kde.kdeconnect.Backends.BaseLinkProvider;
0023 import org.kde.kdeconnect.Helpers.DeviceHelper;
0024 import org.kde.kdeconnect.Helpers.LifecycleHelper;
0025 import org.kde.kdeconnect.Helpers.NotificationHelper;
0026 import org.kde.kdeconnect.Helpers.SecurityHelpers.RsaHelper;
0027 import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
0028 import org.kde.kdeconnect.Plugins.Plugin;
0029 import org.kde.kdeconnect.Plugins.PluginFactory;
0030 import org.kde.kdeconnect.UserInterface.MainActivity;
0031 import org.kde.kdeconnect.UserInterface.ThemeUtil;
0032 import org.kde.kdeconnect_tp.BuildConfig;
0033 import org.slf4j.impl.HandroidLoggerAdapter;
0034 
0035 import java.security.cert.CertificateException;
0036 import java.util.Set;
0037 import java.util.concurrent.ConcurrentHashMap;
0038 
0039 /*
0040  * This class holds all the active devices and makes them accessible from every other class.
0041  * It also takes care of initializing all classes that need so when the app boots.
0042  * It provides a ConnectionReceiver that the BackgroundService uses to ping this class every time a new DeviceLink is created.
0043  */
0044 public class KdeConnect extends Application {
0045 
0046     public interface DeviceListChangedCallback {
0047         void onDeviceListChanged();
0048     }
0049 
0050     private static KdeConnect instance = null;
0051 
0052     private final ConcurrentHashMap<String, Device> devices = new ConcurrentHashMap<>();
0053 
0054     private final ConcurrentHashMap<String, DeviceListChangedCallback> deviceListChangedCallbacks = new ConcurrentHashMap<>();
0055 
0056     @Override
0057     public void onCreate() {
0058         super.onCreate();
0059         instance = this;
0060         setupSL4JLogging();
0061         Log.d("KdeConnect/Application", "onCreate");
0062         ThemeUtil.setUserPreferredTheme(this);
0063         DeviceHelper.initializeDeviceId(this);
0064         RsaHelper.initialiseRsaKeys(this);
0065         SslHelper.initialiseCertificate(this);
0066         PluginFactory.initPluginInfo(this);
0067         NotificationHelper.initializeChannels(this);
0068         LifecycleHelper.initializeObserver();
0069         loadRememberedDevicesFromSettings();
0070 
0071     }
0072 
0073     private void setupSL4JLogging() {
0074         HandroidLoggerAdapter.DEBUG = BuildConfig.DEBUG;
0075         HandroidLoggerAdapter.ANDROID_API_LEVEL = Build.VERSION.SDK_INT;
0076         HandroidLoggerAdapter.APP_NAME = "KDEConnect";
0077     }
0078 
0079     @Override
0080     public void onTerminate() {
0081         Log.d("KdeConnect/Application", "onTerminate");
0082         super.onTerminate();
0083     }
0084 
0085     public void addDeviceListChangedCallback(String key, DeviceListChangedCallback callback) {
0086         deviceListChangedCallbacks.put(key, callback);
0087     }
0088 
0089     public void removeDeviceListChangedCallback(String key) {
0090         deviceListChangedCallbacks.remove(key);
0091     }
0092 
0093     private void onDeviceListChanged() {
0094         Log.i("MainActivity","Device list changed, notifying "+ deviceListChangedCallbacks.size() +" observers.");
0095         for (DeviceListChangedCallback callback : deviceListChangedCallbacks.values()) {
0096             callback.onDeviceListChanged();
0097         }
0098     }
0099 
0100     public ConcurrentHashMap<String, Device> getDevices() {
0101         return devices;
0102     }
0103 
0104     public Device getDevice(String id) {
0105         if (id == null) {
0106             return null;
0107         }
0108         return devices.get(id);
0109     }
0110 
0111     public <T extends Plugin> T getDevicePlugin(String deviceId, Class<T> pluginClass) {
0112         if (deviceId == null) {
0113             return null;
0114         }
0115         Device device = devices.get(deviceId);
0116         if (device == null) {
0117             return null;
0118         }
0119         return device.getPlugin(pluginClass);
0120     }
0121 
0122     public static KdeConnect getInstance() {
0123         return instance;
0124     }
0125 
0126     private void loadRememberedDevicesFromSettings() {
0127         //Log.e("BackgroundService", "Loading remembered trusted devices");
0128         SharedPreferences preferences = getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
0129         Set<String> trustedDevices = preferences.getAll().keySet();
0130         for (String deviceId : trustedDevices) {
0131             //Log.e("BackgroundService", "Loading device "+deviceId);
0132             if (preferences.getBoolean(deviceId, false)) {
0133                 try {
0134                     Device device = new Device(this, deviceId);
0135                     devices.put(deviceId, device);
0136                     device.addPairingCallback(devicePairingCallback);
0137                 } catch (CertificateException e) {
0138                     Log.w("KdeConnect", "Couldn't load the certificate for a remembered device. Removing from trusted list.");
0139                     e.printStackTrace();
0140                     preferences.edit().remove(deviceId).apply();
0141                 }
0142             }
0143         }
0144     }
0145 
0146     private final PairingHandler.PairingCallback devicePairingCallback = new PairingHandler.PairingCallback() {
0147         @Override
0148         public void incomingPairRequest() {
0149             onDeviceListChanged();
0150         }
0151 
0152         @Override
0153         public void pairingSuccessful() {
0154             onDeviceListChanged();
0155         }
0156 
0157         @Override
0158         public void pairingFailed(String error) {
0159             onDeviceListChanged();
0160         }
0161 
0162         @Override
0163         public void unpaired() {
0164             onDeviceListChanged();
0165         }
0166     };
0167 
0168     private final BaseLinkProvider.ConnectionReceiver connectionListener = new BaseLinkProvider.ConnectionReceiver() {
0169         @Override
0170         public void onConnectionReceived(@NonNull final BaseLink link) {
0171             Device device = devices.get(link.getDeviceId());
0172             if (device != null) {
0173                 device.addLink(link);
0174             } else {
0175                 device = new Device(KdeConnect.this, link);
0176                 devices.put(link.getDeviceId(), device);
0177                 device.addPairingCallback(devicePairingCallback);
0178             }
0179             onDeviceListChanged();
0180         }
0181 
0182         @Override
0183         public void onConnectionLost(BaseLink link) {
0184             Device device = devices.get(link.getDeviceId());
0185             Log.i("KDE/onConnectionLost", "removeLink, deviceId: " + link.getDeviceId());
0186             if (device != null) {
0187                 device.removeLink(link);
0188                 if (!device.isReachable() && !device.isPaired()) {
0189                     //Log.e("onConnectionLost","Removing connection device because it was not paired");
0190                     devices.remove(link.getDeviceId());
0191                     device.removePairingCallback(devicePairingCallback);
0192                 }
0193             } else {
0194                 Log.d("KDE/onConnectionLost","Removing connection to unknown device");
0195             }
0196             onDeviceListChanged();
0197         }
0198     };
0199 
0200     public BaseLinkProvider.ConnectionReceiver getConnectionListener() {
0201         return connectionListener;
0202     }
0203 
0204 }