Przeglądaj źródła

Updated sample app with new umbrella aar

Eugene Fryntov 9 lat temu
rodzic
commit
13c163a461

+ 2 - 2
MobileLibrary/Android/SampleApps/TunneledWebView/.idea/vcs.xml → MobileLibrary/Android/SampleApps/TunneledWebView/.idea/encodings.xml

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <?xml version="1.0" encoding="UTF-8"?>
 <project version="4">
 <project version="4">
-  <component name="VcsDirectoryMappings">
-    <mapping directory="" vcs="" />
+  <component name="Encoding">
+    <file url="PROJECT" charset="UTF-8" />
   </component>
   </component>
 </project>
 </project>

+ 6 - 0
MobileLibrary/Android/SampleApps/TunneledWebView/.idea/gradle.xml

@@ -13,6 +13,12 @@
             <option value="$PROJECT_DIR$/app" />
             <option value="$PROJECT_DIR$/app" />
           </set>
           </set>
         </option>
         </option>
+        <option name="myModules">
+          <set>
+            <option value="$PROJECT_DIR$" />
+            <option value="$PROJECT_DIR$/app" />
+          </set>
+        </option>
       </GradleProjectSettings>
       </GradleProjectSettings>
     </option>
     </option>
   </component>
   </component>

+ 8 - 11
MobileLibrary/Android/SampleApps/TunneledWebView/README.md

@@ -10,7 +10,7 @@ an Android app. TunneledWebView proxies a WebView through the Psiphon tunnel.
 Integration
 Integration
 --------------------------------------------------------------------------------
 --------------------------------------------------------------------------------
 
 
-Uses the [Psiphon Android Library](../../AndroidLibrary/README.md).
+Uses the [Psiphon Android Library](../../../Android/README.md).
 
 
 Integration is illustrated in the main activity source file in the sample app. Here are the key parts.
 Integration is illustrated in the main activity source file in the sample app. Here are the key parts.
 
 
@@ -53,16 +53,11 @@ import ca.psiphon.PsiphonTunnel;
 //
 //
 // - Add the Psiphon Library AAR module as a dependency (see this app's
 // - Add the Psiphon Library AAR module as a dependency (see this app's
 //   project settings; to build this sample project, you need to drop
 //   project settings; to build this sample project, you need to drop
-//   psi-0.0.10.aar into app/libs).
-//
-// - Use app/src/main/java/ca/psiphon/PsiphonTunnel.java, which provides
-//   a higher-level wrapper around the Psiphon Library module. This file
-//   shows how to use PsiphonTunnel and PsiphonTunnel.TunneledApp.
-//
+//   ca.psiphon.aar into app/libs).
 //----------------------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------------------
 
 
 public class MainActivity extends ActionBarActivity
 public class MainActivity extends ActionBarActivity
-        implements PsiphonTunnel.TunneledApp {
+        implements PsiphonTunnel.HostService {
 
 
 // ...
 // ...
 
 
@@ -84,7 +79,9 @@ public class MainActivity extends ActionBarActivity
         // Psiphon running, so start/stop in onCreate/onDestroy or
         // Psiphon running, so start/stop in onCreate/onDestroy or
         // even consider running a background Service.
         // even consider running a background Service.
 
 
-        if (!mPsiphonTunnel.start("")) {
+        try {
+            mPsiphonTunnel.startTunneling("");
+        } catch (PsiphonTunnel.Exception e) {
             logMessage("failed to start Psiphon");
             logMessage("failed to start Psiphon");
         }
         }
     }
     }
@@ -114,7 +111,7 @@ public class MainActivity extends ActionBarActivity
 
 
     private void loadWebView() {
     private void loadWebView() {
 
 
-        // NOTE: functions called via PsiphonTunnel.TunneledApp may be
+        // NOTE: functions called via PsiphonTunnel.HostService may be
         // called on background threads. It's important to ensure that
         // called on background threads. It's important to ensure that
         // these threads are not blocked and that UI functions are not
         // these threads are not blocked and that UI functions are not
         // called directly from these threads. Here we use runOnUiThread
         // called directly from these threads. Here we use runOnUiThread
@@ -132,7 +129,7 @@ public class MainActivity extends ActionBarActivity
     // ...
     // ...
 
 
     //----------------------------------------------------------------------------------------------
     //----------------------------------------------------------------------------------------------
-    // PsiphonTunnel.TunneledApp implementation
+    // PsiphonTunnel.HostService implementation
     //
     //
     // NOTE: these are callbacks from the Psiphon Library
     // NOTE: these are callbacks from the Psiphon Library
     //----------------------------------------------------------------------------------------------
     //----------------------------------------------------------------------------------------------

+ 1 - 1
MobileLibrary/Android/SampleApps/TunneledWebView/app/build.gradle

@@ -29,5 +29,5 @@ dependencies {
     compile fileTree(dir: 'libs', include: ['*.jar'])
     compile fileTree(dir: 'libs', include: ['*.jar'])
     testCompile 'junit:junit:4.12'
     testCompile 'junit:junit:4.12'
     compile 'com.android.support:appcompat-v7:21.0.3'
     compile 'com.android.support:appcompat-v7:21.0.3'
-    compile 'go.psi:psi:0.0.10@aar'
+    compile(name:'ca.psiphon', ext:'aar')
 }
 }

+ 0 - 408
MobileLibrary/Android/SampleApps/TunneledWebView/app/src/main/java/ca/psiphon/PsiphonTunnel.java

@@ -1,408 +0,0 @@
-/*
- * Copyright (c) 2016, Psiphon Inc.
- * All rights reserved.
- *
- * 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 3 of the License, or
- * (at your option) any later version.
- *
- * 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/>.
- *
- */
-
-package ca.psiphon;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.os.Build;
-import android.telephony.TelephonyManager;
-import android.util.Base64;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.List;
-import java.util.Locale;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import go.psi.Psi;
-
-public class PsiphonTunnel extends Psi.PsiphonProvider.Stub {
-
-    public interface TunneledApp {
-        Context getContext();
-        String getPsiphonConfig();
-        void onDiagnosticMessage(String message);
-        void onAvailableEgressRegions(List<String> regions);
-        void onSocksProxyPortInUse(int port);
-        void onHttpProxyPortInUse(int port);
-        void onListeningSocksProxyPort(int port);
-        void onListeningHttpProxyPort(int port);
-        void onUpstreamProxyError(String message);
-        void onConnecting();
-        void onConnected();
-        void onHomepage(String url);
-        void onClientRegion(String region);
-        void onClientUpgradeDownloaded(String filename);
-        void onSplitTunnelRegion(String region);
-        void onUntunneledAddress(String address);
-        void onBytesTransferred(long sent, long received);
-        void onStartedWaitingForNetworkConnectivity();
-    }
-
-    private final TunneledApp mTunneledApp;
-    private AtomicBoolean mIsWaitingForNetworkConnectivity;
-
-    // Only one PsiphonVpn instance may exist at a time, as the underlying
-    // go.psi.Psi contains global state.
-    private static PsiphonTunnel mPsiphonTunnel;
-
-    public static synchronized PsiphonTunnel newPsiphonTunnel(TunneledApp tunneledApp) {
-        if (mPsiphonTunnel != null) {
-            mPsiphonTunnel.stop();
-        }
-        // Load the native go code embedded in psi.aar
-        System.loadLibrary("gojni");
-        mPsiphonTunnel = new PsiphonTunnel(tunneledApp);
-        return mPsiphonTunnel;
-    }
-
-    private PsiphonTunnel(TunneledApp tunneledApp) {
-        mTunneledApp = tunneledApp;
-        mIsWaitingForNetworkConnectivity = new AtomicBoolean(false);
-    }
-
-    public Object clone() throws CloneNotSupportedException {
-        throw new CloneNotSupportedException();
-    }
-
-    //----------------------------------------------------------------------------------------------
-    // Public API
-    //----------------------------------------------------------------------------------------------
-
-    public synchronized boolean start(String embeddedServerEntries) {
-        return startPsiphon(embeddedServerEntries);
-    }
-
-    public synchronized void stop() {
-        stopPsiphon();
-    }
-
-    //----------------------------------------------------------------------------------------------
-    // PsiphonProvider (Core support) interface implementation
-    //----------------------------------------------------------------------------------------------
-
-    @Override
-    public void Notice(String noticeJSON) {
-        handlePsiphonNotice(noticeJSON);
-    }
-
-    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
-    @Override
-    public void BindToDevice(long fileDescriptor) throws Exception {
-        // This PsiphonProvider function is only called in TunnelWholeDevice mode
-        throw new Exception("BindToDevice not supported");
-    }
-
-    @Override
-    public long HasNetworkConnectivity() {
-        boolean hasConnectivity = hasNetworkConnectivity(mTunneledApp.getContext());
-        boolean wasWaitingForNetworkConnectivity = mIsWaitingForNetworkConnectivity.getAndSet(!hasConnectivity);
-        if (!hasConnectivity && !wasWaitingForNetworkConnectivity) {
-            // HasNetworkConnectivity may be called many times, but only call
-            // onStartedWaitingForNetworkConnectivity once per loss of connectivity,
-            // so the HostService may log a single message.
-            mTunneledApp.onStartedWaitingForNetworkConnectivity();
-        }
-        // TODO: change to bool return value once gobind supports that type
-        return hasConnectivity ? 1 : 0;
-    }
-
-    private static boolean hasNetworkConnectivity(Context context) {
-        ConnectivityManager connectivityManager =
-                (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
-        NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
-        return networkInfo != null && networkInfo.isConnected();
-    }
-
-    @Override
-    public String GetPrimaryDnsServer() {
-        // This PsiphonProvider function is only called in TunnelWholeDevice mode
-        return "";
-    }
-
-    @Override
-    public String GetSecondaryDnsServer() {
-        // This PsiphonProvider function is only called in TunnelWholeDevice mode
-        return "";
-    }
-
-    //----------------------------------------------------------------------------------------------
-    // Psiphon Tunnel Core
-    //----------------------------------------------------------------------------------------------
-
-    private boolean startPsiphon(String embeddedServerEntries) {
-        stopPsiphon();
-        mTunneledApp.onDiagnosticMessage("starting Psiphon library");
-        try {
-            Psi.Start(
-                    loadPsiphonConfig(mTunneledApp.getContext()),
-                    embeddedServerEntries,
-                    this,
-                    false);
-        } catch (java.lang.Exception e) {
-            mTunneledApp.onDiagnosticMessage("failed to start Psiphon library: " + e.getMessage());
-            return false;
-        }
-        mTunneledApp.onDiagnosticMessage("Psiphon library started");
-        return true;
-    }
-
-    private void stopPsiphon() {
-        mTunneledApp.onDiagnosticMessage("stopping Psiphon library");
-        Psi.Stop();
-        mTunneledApp.onDiagnosticMessage("Psiphon library stopped");
-    }
-
-    private String loadPsiphonConfig(Context context)
-            throws IOException, JSONException {
-
-        // Load settings from the raw resource JSON config file and
-        // update as necessary. Then write JSON to disk for the Go client.
-        JSONObject json = new JSONObject(mTunneledApp.getPsiphonConfig());
-
-        // On Android, these directories must be set to the app private storage area.
-        // The Psiphon library won't be able to use its current working directory
-        // and the standard temporary directories do not exist.
-        json.put("DataStoreDirectory", context.getFilesDir());
-
-        File remoteServerListDownload = new File(context.getFilesDir(), "remote_server_list");
-        json.put("RemoteServerListDownloadFilename", remoteServerListDownload.getPath());
-
-        // Note: onConnecting/onConnected logic assumes 1 tunnel connection
-        json.put("TunnelPoolSize", 1);
-
-        // Continue to run indefinitely until connected
-        json.put("EstablishTunnelTimeoutSeconds", 0);
-
-        // This parameter is for stats reporting
-        json.put("TunnelWholeDevice", 0);
-
-        json.put("EmitBytesTransferred", true);
-
-        json.put("UseIndistinguishableTLS", true);
-
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
-            json.put("UseTrustedCACertificatesForStockTLS", true);
-        }
-
-        try {
-            // Also enable indistinguishable TLS for HTTPS requests that
-            // require system CAs.
-            json.put(
-                    "TrustedCACertificatesFilename",
-                    setupTrustedCertificates(mTunneledApp.getContext()));
-        } catch (Exception e) {
-            mTunneledApp.onDiagnosticMessage(e.getMessage());
-        }
-
-        json.put("DeviceRegion", getDeviceRegion(mTunneledApp.getContext()));
-
-        return json.toString();
-    }
-
-    private void handlePsiphonNotice(String noticeJSON) {
-        try {
-            // All notices are sent on as diagnostic messages
-            // except those that may contain private user data.
-            boolean diagnostic = true;
-
-            JSONObject notice = new JSONObject(noticeJSON);
-            String noticeType = notice.getString("noticeType");
-
-            if (noticeType.equals("Tunnels")) {
-                int count = notice.getJSONObject("data").getInt("count");
-                if (count > 0) {
-                    mTunneledApp.onConnected();
-                } else {
-                    mTunneledApp.onConnecting();
-                }
-
-            } else if (noticeType.equals("AvailableEgressRegions")) {
-                JSONArray egressRegions = notice.getJSONObject("data").getJSONArray("regions");
-                ArrayList<String> regions = new ArrayList<String>();
-                for (int i=0; i<egressRegions.length(); i++) {
-                    regions.add(egressRegions.getString(i));
-                }
-                mTunneledApp.onAvailableEgressRegions(regions);
-
-            } else if (noticeType.equals("SocksProxyPortInUse")) {
-                mTunneledApp.onSocksProxyPortInUse(notice.getJSONObject("data").getInt("port"));
-
-            } else if (noticeType.equals("HttpProxyPortInUse")) {
-                mTunneledApp.onHttpProxyPortInUse(notice.getJSONObject("data").getInt("port"));
-
-            } else if (noticeType.equals("ListeningSocksProxyPort")) {
-                int port = notice.getJSONObject("data").getInt("port");
-                mTunneledApp.onListeningSocksProxyPort(port);
-
-            } else if (noticeType.equals("ListeningHttpProxyPort")) {
-                int port = notice.getJSONObject("data").getInt("port");
-                mTunneledApp.onListeningHttpProxyPort(port);
-
-            } else if (noticeType.equals("UpstreamProxyError")) {
-                mTunneledApp.onUpstreamProxyError(notice.getJSONObject("data").getString("message"));
-
-            } else if (noticeType.equals("ClientUpgradeDownloaded")) {
-                mTunneledApp.onClientUpgradeDownloaded(notice.getJSONObject("data").getString("filename"));
-
-            } else if (noticeType.equals("Homepage")) {
-                mTunneledApp.onHomepage(notice.getJSONObject("data").getString("url"));
-
-            } else if (noticeType.equals("ClientRegion")) {
-                mTunneledApp.onClientRegion(notice.getJSONObject("data").getString("region"));
-
-            } else if (noticeType.equals("SplitTunnelRegion")) {
-                mTunneledApp.onSplitTunnelRegion(notice.getJSONObject("data").getString("region"));
-
-            } else if (noticeType.equals("UntunneledAddress")) {
-                mTunneledApp.onUntunneledAddress(notice.getJSONObject("data").getString("address"));
-
-            } else if (noticeType.equals("BytesTransferred")) {
-                diagnostic = false;
-                JSONObject data = notice.getJSONObject("data");
-                mTunneledApp.onBytesTransferred(data.getLong("sent"), data.getLong("received"));
-            }
-
-            if (diagnostic) {
-                String diagnosticMessage = noticeType + ": " + notice.getJSONObject("data").toString();
-                mTunneledApp.onDiagnosticMessage(diagnosticMessage);
-            }
-
-        } catch (JSONException e) {
-            // Ignore notice
-        }
-    }
-
-    private String setupTrustedCertificates(Context context) throws Exception {
-
-        // Copy the Android system CA store to a local, private cert bundle file.
-        //
-        // This results in a file that can be passed to SSL_CTX_load_verify_locations
-        // for use with OpenSSL modes in tunnel-core.
-        // https://www.openssl.org/docs/manmaster/ssl/SSL_CTX_load_verify_locations.html
-        //
-        // TODO: to use the path mode of load_verify_locations would require emulating
-        // the filename scheme used by c_rehash:
-        // https://www.openssl.org/docs/manmaster/apps/c_rehash.html
-        // http://stackoverflow.com/questions/19237167/the-new-subject-hash-openssl-algorithm-differs
-
-        File directory = context.getDir("PsiphonCAStore", Context.MODE_PRIVATE);
-
-        final String errorMessage = "copy AndroidCAStore failed";
-        try {
-
-            File file = new File(directory, "certs.dat");
-
-            // Pave a fresh copy on every run, which ensures we're not using old certs.
-            // Note: assumes KeyStore doesn't return revoked certs.
-            //
-            // TODO: this takes under 1 second, but should we avoid repaving every time?
-            file.delete();
-
-            PrintStream output = null;
-            try {
-                output = new PrintStream(new FileOutputStream(file));
-
-                KeyStore keyStore;
-                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
-                    keyStore = KeyStore.getInstance("AndroidCAStore");
-                    keyStore.load(null, null);
-                } else {
-                    keyStore = KeyStore.getInstance("BKS");
-                    FileInputStream inputStream = new FileInputStream("/etc/security/cacerts.bks");
-                    try {
-                        keyStore.load(inputStream, "changeit".toCharArray());
-                    } finally {
-                        if (inputStream != null) {
-                            inputStream.close();
-                        }
-                    }
-                }
-
-                Enumeration<String> aliases = keyStore.aliases();
-                while (aliases.hasMoreElements()) {
-                    String alias = aliases.nextElement();
-                    X509Certificate cert = (X509Certificate) keyStore.getCertificate(alias);
-
-                    output.println("-----BEGIN CERTIFICATE-----");
-                    String pemCert = new String(Base64.encode(cert.getEncoded(), Base64.NO_WRAP), "UTF-8");
-                    // OpenSSL appears to reject the default linebreaking done by Base64.encode,
-                    // so we manually linebreak every 64 characters
-                    for (int i = 0; i < pemCert.length() ; i+= 64) {
-                        output.println(pemCert.substring(i, Math.min(i + 64, pemCert.length())));
-                    }
-                    output.println("-----END CERTIFICATE-----");
-                }
-
-                mTunneledApp.onDiagnosticMessage("prepared PsiphonCAStore");
-
-                return file.getAbsolutePath();
-
-            } finally {
-                if (output != null) {
-                    output.close();
-                }
-            }
-
-        } catch (KeyStoreException e) {
-            throw new Exception(errorMessage, e);
-        } catch (NoSuchAlgorithmException e) {
-            throw new Exception(errorMessage, e);
-        } catch (CertificateException e) {
-            throw new Exception(errorMessage, e);
-        } catch (IOException e) {
-            throw new Exception(errorMessage, e);
-        }
-    }
-
-    private static String getDeviceRegion(Context context) {
-        String region = "";
-        TelephonyManager telephonyManager = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
-        if (telephonyManager != null) {
-            region = telephonyManager.getSimCountryIso();
-            if (region.length() == 0 && telephonyManager.getPhoneType() != TelephonyManager.PHONE_TYPE_CDMA) {
-                region = telephonyManager.getNetworkCountryIso();
-            }
-        }
-        if (region.length() == 0) {
-            Locale defaultLocale = Locale.getDefault();
-            if (defaultLocale != null) {
-                region = defaultLocale.getCountry();
-            }
-        }
-        return region.toUpperCase(Locale.US);
-    }
-}

+ 35 - 2
MobileLibrary/Android/SampleApps/TunneledWebView/app/src/main/java/ca/psiphon/tunneledwebview/MainActivity.java

@@ -72,7 +72,7 @@ import ca.psiphon.PsiphonTunnel;
 //----------------------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------------------
 
 
 public class MainActivity extends ActionBarActivity
 public class MainActivity extends ActionBarActivity
-        implements PsiphonTunnel.TunneledApp {
+        implements PsiphonTunnel.HostService {
 
 
     private ListView mListView;
     private ListView mListView;
     private WebView mWebView;
     private WebView mWebView;
@@ -110,7 +110,10 @@ public class MainActivity extends ActionBarActivity
         // Psiphon running, so start/stop in onCreate/onDestroy or
         // Psiphon running, so start/stop in onCreate/onDestroy or
         // even consider running a background Service.
         // even consider running a background Service.
 
 
-        if (!mPsiphonTunnel.start("")) {
+
+        try {
+            mPsiphonTunnel.startTunneling("");
+        } catch (PsiphonTunnel.Exception e) {
             logMessage("failed to start Psiphon");
             logMessage("failed to start Psiphon");
         }
         }
     }
     }
@@ -170,11 +173,26 @@ public class MainActivity extends ActionBarActivity
     // NOTE: these are callbacks from the Psiphon Library
     // NOTE: these are callbacks from the Psiphon Library
     //----------------------------------------------------------------------------------------------
     //----------------------------------------------------------------------------------------------
 
 
+    @Override
+    public String getAppName() {
+        return "TunneledWebView Sample";
+    }
+
     @Override
     @Override
     public Context getContext() {
     public Context getContext() {
         return this;
         return this;
     }
     }
 
 
+    @Override
+    public Object getVpnService() {
+        return null;
+    }
+
+    @Override
+    public Object newVpnServiceBuilder() {
+        return null;
+    }
+
     @Override
     @Override
     public String getPsiphonConfig() {
     public String getPsiphonConfig() {
         try {
         try {
@@ -252,6 +270,11 @@ public class MainActivity extends ActionBarActivity
         logMessage("client upgrade downloaded");
         logMessage("client upgrade downloaded");
     }
     }
 
 
+    @Override
+    public void onClientIsLatestVersion() {
+
+    }
+
     @Override
     @Override
     public void onSplitTunnelRegion(String region) {
     public void onSplitTunnelRegion(String region) {
         logMessage("split tunnel region: " + region);
         logMessage("split tunnel region: " + region);
@@ -273,6 +296,16 @@ public class MainActivity extends ActionBarActivity
         logMessage("waiting for network connectivity...");
         logMessage("waiting for network connectivity...");
     }
     }
 
 
+    @Override
+    public void onClientVerificationRequired(String s, int i, boolean b) {
+
+    }
+
+    @Override
+    public void onExiting() {
+
+    }
+
     @Override
     @Override
     public void onClientRegion(String region) {
     public void onClientRegion(String region) {
         logMessage("client region: " + region);
         logMessage("client region: " + region);