This repository has been archived by the owner on Jan 15, 2021. It is now read-only.
/
ThaliDeviceHubService.java
130 lines (119 loc) · 6.54 KB
/
ThaliDeviceHubService.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/*
Copyright (c) Microsoft Open Technologies, Inc.
All Rights Reserved
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED,
INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache 2 License for the specific language governing permissions and limitations under the License.
*/
package com.msopentech.thali.devicehub.android;
import android.app.Notification;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import com.couchbase.lite.android.AndroidContext;
import com.couchbase.lite.util.Log;
import com.msopentech.thali.CouchDBListener.ThaliListener;
import com.msopentech.thali.android.toronionproxy.AndroidOnionProxyManager;
import com.msopentech.thali.utilities.universal.CblLogTags;
import java.io.IOException;
import java.net.UnknownHostException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
/**
* The service is marked false for exported and has no intents so it has to be called directly by class. In theory
* because of these two facts the exported value is not needed since the lack of intents sets it to false by default
* but whatever, I'll be paranoid. In any case the service is only to be started and invoked (in an Android sense)
* by the hub application. Anyone else who wants to communicate to the service should do it over the wire, including
* local apps.
*
* The service, once started, runs forever. It also ignores whatever intent is sent in, it just starts. Which is
* why it returns START_STICKY which says that if the system needs to kill the server to retrieve memory then
* the service should be restarted with a null intent. We don't care what the actual intent was that started the
* service since the only action we take is to run the CouchDB server.
*
* We are explicitly using a start service and not a bound service under the potentially false impression that
* we want the service to run even if the user should turn off the Thali Device Hub application. The service is
* core to Thali and all apps need it. In addition it needs to run all the time (or some reasonably facsimile there
* of depending on battery) so that incoming requests from off device can be handled. So a bound service doesn't
* make sense because then we would only run while some local app wants us to.
*
* It's tempting to argue this should be a foreground service since it will eat battery and users should know its
* there and be able to kill it. But our goal is that the service 'just works' so the user shouldn't need to be
* aware of it. To the extent there are battery issues it is our job to manage them for the user by running
* less frequently. We will support turning the service off via the TDH Application UX but otherwise we shouldn't
* claim the foreground service role.
*/
public class ThaliDeviceHubService extends Service {
public static final String HttpKeysNotification = "com.msopentech.thali.devicehub.android.httpkeys";
public static final String LocalMachineIPHttpKeyURLName = "LocalMachineIPHttpKeyURL";
public static final String OnionHttpKeyURLName = "OnionHttpKeyURLName";
public static final String SocksOnionProxyPort = "SocksOnionProxyPort";
protected ThaliListener thaliListener = null;
// These values are used for testing
public volatile boolean thaliListenerRunning = false;
public volatile boolean httpKeysSentByBroadcast = false;
@Override
public void onCreate() {
// Embarrassing enough I'm not sure if getApplicationContext is the right context to get. :(
Context context = getApplicationContext();
AndroidOnionProxyManager androidOnionProxyManager = new AndroidOnionProxyManager(context, "TorOnionProxy");
thaliListener = new ThaliListener();
try {
thaliListener.startServer(new AndroidContext(context), ThaliListener.DefaultThaliDeviceHubPort,
androidOnionProxyManager);
thaliListenerRunning = true;
return;
} catch (UnrecoverableKeyException e) {
Log.e(CblLogTags.TAG_THALI_LISTENER, "Couldn't start", e);
} catch (NoSuchAlgorithmException e) {
Log.e(CblLogTags.TAG_THALI_LISTENER, "Couldn't start", e);
} catch (KeyStoreException e) {
Log.e(CblLogTags.TAG_THALI_LISTENER, "Couldn't start", e);
}
thaliListenerRunning = false;
}
@Override
public void onDestroy() {
if (thaliListener != null) {
thaliListener.stopServer();
thaliListenerRunning = false;
httpKeysSentByBroadcast = false;
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// onStartCommand is called on the main thread and getHttpKeys calls getStatus which blocks so we
// spawn another thread.
new Thread(new Runnable() {
@Override
public void run() {
Intent httpKeysIntent = new Intent(HttpKeysNotification);
try {
httpKeysIntent.putExtra(LocalMachineIPHttpKeyURLName, thaliListener.getHttpKeys().getLocalMachineIPHttpKeyURL());
httpKeysIntent.putExtra(OnionHttpKeyURLName, thaliListener.getHttpKeys().getOnionHttpKeyURL());
httpKeysIntent.putExtra(SocksOnionProxyPort, thaliListener.getHttpKeys().getSocksOnionProxyPort());
sendBroadcast(httpKeysIntent);
httpKeysSentByBroadcast = true;
return;
} catch (InterruptedException e) {
Log.e(CblLogTags.TAG_THALI_LISTENER, "We could not get http keys from the listener.", e);
} catch (UnknownHostException e) {
Log.e(CblLogTags.TAG_THALI_LISTENER, "We could not get http keys from the listener.", e);
} catch (IOException e) {
Log.e(CblLogTags.TAG_THALI_LISTENER, "We could not get http keys from the listener.", e);
}
httpKeysSentByBroadcast = false;
}
}).start();
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}