1TunneledWebView README
2================================================================================
3
4Overview
5--------------------------------------------------------------------------------
6
7TunneledWebView is a sample app that demonstrates embedding the Psiphon Library in
8an Android app. TunneledWebView proxies a WebView through the Psiphon tunnel.
9
10Caveats
11--------------------------------------------------------------------------------
12
13### i18n API Leaks Timezone
14
15The Internationalization API (i18n) provides websites, though a JavaScript API, with access to the timezone used by
16the user's browser (in this case WebView). This does not reveal the precise location of the user, but can be accurate
17enough to identify the city in which the user is located.
18
19The i18n API cannot be disabled without disabling JavaScript.
20
21### Untunneled WebRTC
22
23WebRTC requests do not use the configured proxy settings of a WebView. JavaScript must be disabled in a WebView to
24effectively disable WebRTC. If not disabled, WebRTC will leak the untunneled client IP address and the WebRTC connection
25may be performed entirely outside of the tunnel.
26
27One solution would be to use a WebRTC library which allows setting a proxy; or use
28[Mozilla's GeckoView](https://wiki.mozilla.org/Mobile/GeckoView), which is a WebView alternative which allows disabling
29WebRTC.
30
31Integration
32--------------------------------------------------------------------------------
33
34Uses the [Psiphon Android Library](../../../Android/README.md).
35
36Integration is illustrated in the main activity source file in the sample app. Here are the key parts.
37
38```Java
39
40/*
41 * Copyright (c) 2016, Psiphon Inc.
42 * All rights reserved.
43 */
44
45package ca.psiphon.tunneledwebview;
46
47// ...
48
49import ca.psiphon.PsiphonTunnel;
50
51//----------------------------------------------------------------------------------------------
52// TunneledWebView
53//
54// This sample app demonstrates tunneling a WebView through the
55// Psiphon Library. This app's main activity shows a log of
56// events and a WebView that is loaded once Psiphon is connected.
57//
58// The flow is as follows:
59//
60// - The Psiphon tunnel is started in onResume(). PsiphonTunnel.startTunneling()
61//   is an asynchronous call that returns immediately.
62//
63// - Once Psiphon has selected a local HTTP proxy listening port, the
64//   onListeningHttpProxyPort() callback is called. This app records the
65//   port to use for tunneling traffic.
66//
67// - Once Psiphon has established a tunnel, the onConnected() callback
68//   is called. This app now loads the WebView, after setting its proxy
69//   to point to Psiphon's local HTTP proxy.
70//
71// To adapt this sample into your own app:
72//
73// - Embed a Psiphon config file in app/src/main/res/raw/psiphon_config.
74//
75// - Add the Psiphon Library AAR module as a dependency (see this app's
76//   project settings; to build this sample project, you need to drop
77//   ca.psiphon.aar into app/libs).
78//----------------------------------------------------------------------------------------------
79
80public class MainActivity extends ActionBarActivity
81        implements PsiphonTunnel.HostService {
82
83// ...
84
85    @Override
86    protected void onCreate(Bundle savedInstanceState) {
87
88        // ...
89
90        mPsiphonTunnel = PsiphonTunnel.newPsiphonTunnel(this);
91    }
92
93    @Override
94    protected void onResume() {
95        super.onResume();
96
97        // NOTE: for demonstration purposes, this sample app
98        // restarts Psiphon in onPause/onResume. Since it may take some
99        // time to connect, it's generally recommended to keep
100        // Psiphon running, so start/stop in onCreate/onDestroy or
101        // even consider running a background Service.
102
103        try {
104            mPsiphonTunnel.startTunneling("");
105        } catch (PsiphonTunnel.Exception e) {
106            logMessage("failed to start Psiphon");
107        }
108    }
109
110    @Override
111    protected void onPause() {
112        super.onPause();
113
114        // NOTE: stop() can block for a few seconds, so it's generally
115        // recommended to run PsiphonTunnel.start()/stop() in a background
116        // thread and signal the thread appropriately.
117
118        mPsiphonTunnel.stop();
119    }
120
121    private void setHttpProxyPort(int port) {
122
123        // NOTE: here we record the Psiphon proxy port for subsequent
124        // use in tunneling app traffic. In this sample app, we will
125        // use WebViewProxySettings.setLocalProxy to tunnel a WebView
126        // through Psiphon. By default, the local proxy port is selected
127        // dynamically, so it's important to record and use the correct
128        // port number.
129
130        mLocalHttpProxyPort.set(port);
131    }
132
133    private void loadWebView() {
134
135        // NOTE: functions called via PsiphonTunnel.HostService may be
136        // called on background threads. It's important to ensure that
137        // these threads are not blocked and that UI functions are not
138        // called directly from these threads. Here we use runOnUiThread
139        // to handle this.
140
141        runOnUiThread(new Runnable() {
142            public void run() {
143                WebViewProxySettings.setLocalProxy(
144                        MainActivity.this, mLocalHttpProxyPort.get());
145                mWebView.loadUrl("https://ipinfo.io/");
146            }
147        });
148    }
149
150    // ...
151
152    //----------------------------------------------------------------------------------------------
153    // PsiphonTunnel.HostService implementation
154    //
155    // NOTE: these are callbacks from the Psiphon Library
156    //----------------------------------------------------------------------------------------------
157
158    // ...
159
160    @Override
161    public void onListeningSocksProxyPort(int port) {
162        logMessage("local SOCKS proxy listening on port: " + Integer.toString(port));
163    }
164
165    // ...
166
167    @Override
168    public void onConnected() {
169        logMessage("connected");
170        loadWebView();
171    }
172
173    // ...
174
175}
176
177```
178
179## License
180
181See the [LICENSE](../LICENSE) file.
182