[tor-commits] [orbot/master] re-enable traffic notification info and improve debug output

n8fr8 at torproject.org n8fr8 at torproject.org
Tue Apr 28 21:05:01 UTC 2020


commit 7cd5a2884d3d2980c92b323ad3191afe48736c38
Author: n8fr8 <nathan at guardianproject.info>
Date:   Mon Dec 9 15:45:12 2019 -0500

    re-enable traffic notification info and improve debug output
    - tor entrance IP now displayed in logs if you enable debug option
---
 .../android/service/TorEventHandler.java           | 161 +++++++--------------
 .../org/torproject/android/service/TorService.java |  37 ++---
 .../android/service/util/ExternalIPFetcher.java    |  95 ++++++++++++
 .../android/service/vpn/OrbotVpnManager.java       |  24 ++-
 .../android/service/vpn/TorVpnService.java         |   4 +-
 orbotservice/src/main/res/drawable/ic_onion.xml    |   9 ++
 .../src/main/res/drawable/ic_stat_tor_xfer.png     | Bin 0 -> 990 bytes
 orbotservice/src/main/res/drawable/ic_transfer.xml |   9 ++
 8 files changed, 194 insertions(+), 145 deletions(-)

diff --git a/orbotservice/src/main/java/org/torproject/android/service/TorEventHandler.java b/orbotservice/src/main/java/org/torproject/android/service/TorEventHandler.java
index c67afff4..8635eb7b 100644
--- a/orbotservice/src/main/java/org/torproject/android/service/TorEventHandler.java
+++ b/orbotservice/src/main/java/org/torproject/android/service/TorEventHandler.java
@@ -4,6 +4,7 @@ import android.text.TextUtils;
 
 import net.freehaven.tor.control.EventHandler;
 
+import org.torproject.android.service.util.ExternalIPFetcher;
 import org.torproject.android.service.util.Prefs;
 
 import java.text.NumberFormat;
@@ -32,12 +33,14 @@ public class TorEventHandler implements EventHandler, TorServiceConstants {
 
     public class Node
     {
-        String status;
-        String id;
-        String name;
-        String ipAddress;
-        String country;
-        String organization;
+        public String status;
+        public String id;
+        public String name;
+        public String ipAddress;
+        public String country;
+        public String organization;
+
+        public boolean isFetchingInfo = false;
     }
 
     public HashMap<String,Node> getNodes ()
@@ -102,7 +105,6 @@ public class TorEventHandler implements EventHandler, TorServiceConstants {
 
         if (read != lastRead || written != lastWritten)
         {
-            /**
             StringBuilder sb = new StringBuilder();
             sb.append(formatCount(read));
             sb.append(" \u2193");
@@ -116,7 +118,6 @@ public class TorEventHandler implements EventHandler, TorServiceConstants {
                 iconId = R.drawable.ic_stat_tor_xfer;
 
             mService.showToolbarNotification(sb.toString(), mService.getNotifyId(), iconId);
-             **/
 
             mTotalTrafficWritten += written;
             mTotalTrafficRead += read;
@@ -161,9 +162,12 @@ public class TorEventHandler implements EventHandler, TorServiceConstants {
             StringTokenizer st = new StringTokenizer(path, ",");
             Node node = null;
 
+            boolean isFirstNode = true;
+            int nodeCount = st.countTokens();
+
             while (st.hasMoreTokens()) {
                 String nodePath = st.nextToken();
-                node = new Node();
+                String nodeId = null, nodeName = null;
 
                 String[] nodeParts;
 
@@ -173,129 +177,70 @@ public class TorEventHandler implements EventHandler, TorServiceConstants {
                     nodeParts = nodePath.split("~");
 
                 if (nodeParts.length == 1) {
-                    node.id = nodeParts[0].substring(1);
-                    node.name = node.id;
+                    nodeId = nodeParts[0].substring(1);
+                    nodeName = node.id;
                 } else if (nodeParts.length == 2) {
-                    node.id = nodeParts[0].substring(1);
-                    node.name = nodeParts[1];
+                    nodeId = nodeParts[0].substring(1);
+                    nodeName = nodeParts[1];
+                }
+
+                if (nodeId == null)
+                    continue;
+
+                node = hmBuiltNodes.get(nodeId);
+
+                if (node == null)
+                {
+                    node = new Node();
+                    node.id = nodeId;
+                    node.name = nodeName;
                 }
 
                 node.status = status;
 
                 sb.append(node.name);
 
+                if (!TextUtils.isEmpty(node.ipAddress))
+                    sb.append("(").append(node.ipAddress).append(")");
+
                 if (st.hasMoreTokens())
                     sb.append(" > ");
-            }
 
-            if (Prefs.useDebugLogging())
-                mService.debug(sb.toString());
-            else if (status.equals("BUILT"))
-                mService.logNotice(sb.toString());
-            else if (status.equals("CLOSED"))
-                mService.logNotice(sb.toString());
+                if (status.equals("EXTENDED"))
+                {
 
-            if (Prefs.expandedNotifications()) {
-                //get IP from last nodename
-                if (status.equals("BUILT")) {
+                    if (isFirstNode)
+                    {
+                        hmBuiltNodes.put(node.id, node);
 
-                    // if (node.ipAddress == null)
-                    //   mService.exec(new ExternalIPFetcher(node));
+                        if (node.ipAddress == null && (!node.isFetchingInfo) && Prefs.useDebugLogging()) {
+                            node.isFetchingInfo = true;
+                            mService.exec(new ExternalIPFetcher(mService, node, TorService.mPortHTTP));
+                        }
 
-                    hmBuiltNodes.put(circID, node);
+                        isFirstNode = false;
+                    }
                 }
+                else if (status.equals("BUILT")) {
+                 //   mService.logNotice(sb.toString());
 
-                if (status.equals("CLOSED")) {
-                    hmBuiltNodes.remove(circID);
-
+                    if (Prefs.useDebugLogging() && nodeCount > 3)
+                        mService.debug(sb.toString());
+                }
+                else if (status.equals("CLOSED")) {
+                    //  mService.logNotice(sb.toString());
+                    hmBuiltNodes.remove(node.id);
                 }
-            }
-        }
-
-    }
 
-    /**
-    private class ExternalIPFetcher implements Runnable {
+            }
 
-        private Node mNode;
-        private int MAX_ATTEMPTS = 3;
-        private final static String ONIONOO_BASE_URL = "https://onionoo.torproject.org/details?fields=country_name,as_name,or_addresses&lookup=";
 
-        public ExternalIPFetcher (Node node)
-        {
-            mNode = node;
         }
 
-        public void run ()
-        {
-
-            for (int i = 0; i < MAX_ATTEMPTS; i++)
-            {
-                if (mService.getControlConnection() != null)
-                {
-                    try {
-
-                        URLConnection conn = null;
-
-                        Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 8118));
-                        conn = new URL(ONIONOO_BASE_URL + mNode.id).openConnection(proxy);
-
-                        conn.setRequestProperty("Connection","Close");
-                        conn.setConnectTimeout(60000);
-                        conn.setReadTimeout(60000);
 
-                        InputStream is = conn.getInputStream();
-
-                        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
-
-                        // getting JSON string from URL
-
-                        StringBuffer json = new StringBuffer();
-                        String line = null;
-
-                        while ((line = reader.readLine())!=null)
-                            json.append(line);
-
-                        JSONObject jsonNodeInfo = new org.json.JSONObject(json.toString());
-
-                        JSONArray jsonRelays = jsonNodeInfo.getJSONArray("relays");
-
-                        if (jsonRelays.length() > 0)
-                        {
-                            mNode.ipAddress = jsonRelays.getJSONObject(0).getJSONArray("or_addresses").getString(0).split(":")[0];
-                            mNode.country = jsonRelays.getJSONObject(0).getString("country_name");
-                            mNode.organization = jsonRelays.getJSONObject(0).getString("as_name");
-
-                            StringBuffer sbInfo = new StringBuffer();
-                            sbInfo.append(mNode.ipAddress);
-
-                            if (mNode.country != null)
-                                sbInfo.append(' ').append(mNode.country);
-
-                            if (mNode.organization != null)
-                                sbInfo.append(" (").append(mNode.organization).append(')');
-
-                            mService.logNotice(sbInfo.toString());
-
-                        }
-
-                        reader.close();
-                        is.close();
-
-                        break;
-
-                    } catch (Exception e) {
-
-                        mService.debug ("Error getting node details from onionoo: " + e.getMessage());
-
-
-                    }
-                }
-            }
-        }
 
+    }
 
-    }**/
 
     private String parseNodeName(String node)
     {
diff --git a/orbotservice/src/main/java/org/torproject/android/service/TorService.java b/orbotservice/src/main/java/org/torproject/android/service/TorService.java
index ea807be4..110b562f 100644
--- a/orbotservice/src/main/java/org/torproject/android/service/TorService.java
+++ b/orbotservice/src/main/java/org/torproject/android/service/TorService.java
@@ -37,6 +37,8 @@ import androidx.core.app.NotificationCompat;
 import androidx.localbroadcastmanager.content.LocalBroadcastManager;
 import android.text.TextUtils;
 import android.util.Log;
+import android.widget.RemoteViews;
+
 import com.jaredrummler.android.shell.CommandResult;
 
 import net.freehaven.tor.control.ConfigEntry;
@@ -75,6 +77,7 @@ import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Random;
+import java.util.Set;
 import java.util.StringTokenizer;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -402,7 +405,7 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
 
 
         try {
-            unregisterReceiver(mNetworkStateReceiver);
+         //   unregisterReceiver(mNetworkStateReceiver);
             unregisterReceiver(mActionBroadcastReceiver);
         }
         catch (IllegalArgumentException iae)
@@ -536,8 +539,8 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
                 mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
             }
 
-            IntentFilter mNetworkStateFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
-            registerReceiver(mNetworkStateReceiver , mNetworkStateFilter);
+        //    IntentFilter mNetworkStateFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
+          //  registerReceiver(mNetworkStateReceiver , mNetworkStateFilter);
 
             IntentFilter filter = new IntentFilter();
             filter.addAction(CMD_NEWNYM);
@@ -1472,6 +1475,7 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
      *  BroadcastReciever in the Android manifest.
      */
 
+    /**
     private final BroadcastReceiver mNetworkStateReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -1504,33 +1508,12 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
             if (newConnectivityState != mConnectivity) {
                 mConnectivity = newConnectivityState;
 
-                if (mConnectivity)
-                    newIdentity();
+                //if (mConnectivity)
+                  //  newIdentity();
             }
 
-            /**
-            if (doNetworKSleep && mCurrentStatus != STATUS_OFF)
-            {
-                    setTorNetworkEnabled (mConnectivity);
-
-                if (!mConnectivity)
-                {
-                    logNotice(context.getString(R.string.no_network_connectivity_putting_tor_to_sleep_));
-                    showToolbarNotification(getString(R.string.no_internet_connection_tor),NOTIFY_ID,R.drawable.ic_stat_tor_off);
-
-                }
-                else
-                {
-                    logNotice(context.getString(R.string.network_connectivity_is_good_waking_tor_up_));
-                    showToolbarNotification(getString(R.string.status_activated),NOTIFY_ID,R.drawable.ic_stat_tor);
-
-                }
-
-            }**/
-
-
         }
-    };
+    };**/
 
     private StringBuffer processSettingsImpl (StringBuffer extraLines) throws IOException
     {
diff --git a/orbotservice/src/main/java/org/torproject/android/service/util/ExternalIPFetcher.java b/orbotservice/src/main/java/org/torproject/android/service/util/ExternalIPFetcher.java
new file mode 100644
index 00000000..ae99e7de
--- /dev/null
+++ b/orbotservice/src/main/java/org/torproject/android/service/util/ExternalIPFetcher.java
@@ -0,0 +1,95 @@
+package org.torproject.android.service.util;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.torproject.android.service.TorEventHandler;
+import org.torproject.android.service.TorService;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.URL;
+import java.net.URLConnection;
+
+public class ExternalIPFetcher implements Runnable {
+
+    private TorService mService;
+    private TorEventHandler.Node mNode;
+    private final static String ONIONOO_BASE_URL = "https://onionoo.torproject.org/details?fields=country_name,as_name,or_addresses&lookup=";
+    private int mLocalHttpProxyPort = 8118;
+
+    public ExternalIPFetcher (TorService service, TorEventHandler.Node node, int localProxyPort )
+    {
+        mService = service;
+        mNode = node;
+        mLocalHttpProxyPort = localProxyPort;
+    }
+
+    public void run ()
+    {
+        try {
+
+            URLConnection conn = null;
+
+            Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", mLocalHttpProxyPort));
+            conn = new URL(ONIONOO_BASE_URL + mNode.id).openConnection(proxy);
+
+            conn.setRequestProperty("Connection","Close");
+            conn.setConnectTimeout(60000);
+            conn.setReadTimeout(60000);
+
+            InputStream is = conn.getInputStream();
+
+            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+
+            // getting JSON string from URL
+
+            StringBuffer json = new StringBuffer();
+            String line = null;
+
+            while ((line = reader.readLine())!=null)
+                json.append(line);
+
+            JSONObject jsonNodeInfo = new org.json.JSONObject(json.toString());
+
+            JSONArray jsonRelays = jsonNodeInfo.getJSONArray("relays");
+
+            if (jsonRelays.length() > 0)
+            {
+                mNode.ipAddress = jsonRelays.getJSONObject(0).getJSONArray("or_addresses").getString(0).split(":")[0];
+                mNode.country = jsonRelays.getJSONObject(0).getString("country_name");
+                mNode.organization = jsonRelays.getJSONObject(0).getString("as_name");
+
+                StringBuffer sbInfo = new StringBuffer();
+                sbInfo.append(mNode.name).append("(");
+                sbInfo.append(mNode.ipAddress).append(")");
+
+                if (mNode.country != null)
+                    sbInfo.append(' ').append(mNode.country);
+
+                if (mNode.organization != null)
+                    sbInfo.append(" (").append(mNode.organization).append(')');
+
+                mService.debug(sbInfo.toString());
+
+            }
+
+            reader.close();
+            is.close();
+
+
+
+        } catch (Exception e) {
+
+        //    mService.debug ("Error getting node details from onionoo: " + e.getMessage());
+
+
+        }
+
+
+    }
+
+
+}
\ No newline at end of file
diff --git a/orbotservice/src/main/java/org/torproject/android/service/vpn/OrbotVpnManager.java b/orbotservice/src/main/java/org/torproject/android/service/vpn/OrbotVpnManager.java
index b78c7a4f..c747eb59 100644
--- a/orbotservice/src/main/java/org/torproject/android/service/vpn/OrbotVpnManager.java
+++ b/orbotservice/src/main/java/org/torproject/android/service/vpn/OrbotVpnManager.java
@@ -51,6 +51,7 @@ import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.concurrent.TimeoutException;
 
+import static org.torproject.android.service.TorServiceConstants.ACTION_START;
 import static org.torproject.android.service.vpn.VpnUtils.getSharedPrefs;
 import static org.torproject.android.service.vpn.VpnUtils.killProcess;
 
@@ -101,6 +102,8 @@ public class OrbotVpnManager implements Handler.Callback {
 
 
 	}
+
+	boolean isStarted = false;
    
     //public int onStartCommand(Intent intent, int flags, int startId) {
     public int handleIntent(Builder builder, Intent intent) {
@@ -109,8 +112,10 @@ public class OrbotVpnManager implements Handler.Callback {
     	{
 	    	String action = intent.getAction();
 	    	
-	    	if (action.equals("start"))
+	    	if (action.equals(TorVpnService.ACTION_START))
 	    	{
+				isStarted = true;
+
 		        // Stop the previous session by interrupting the thread.
 		        if (mThreadVPN != null && mThreadVPN.isAlive())
 		        	stopVPN();
@@ -129,8 +134,10 @@ public class OrbotVpnManager implements Handler.Callback {
 
 
 	    	}
-	    	else if (action.equals("stop"))
+	    	else if (action.equals(TorVpnService.ACTION_STOP))
 	    	{
+				isStarted = false;
+
 	    		Log.d(TAG,"stop OrbotVPNService service!");
 	    		
 	    		stopVPN();
@@ -335,12 +342,13 @@ public class OrbotVpnManager implements Handler.Callback {
 		        	isRestart = false;
 
 					//start PDNSD daemon pointing to actual DNS
-					int pdnsdPort = 8091;
-						startDNS(filePdnsd.getCanonicalPath(), localhost,mTorDns, virtualGateway, pdnsdPort);
-					final boolean localDnsTransparentProxy = true;
-
-					Tun2Socks.Start(mService, mInterface, VPN_MTU, virtualIP, virtualNetMask, localSocks , virtualGateway + ":" + pdnsdPort , localDnsTransparentProxy);
+					if (filePdnsd != null) {
+						int pdnsdPort = 8091;
+						startDNS(filePdnsd.getCanonicalPath(), localhost, mTorDns, virtualGateway, pdnsdPort);
+						final boolean localDnsTransparentProxy = true;
 
+						Tun2Socks.Start(mService, mInterface, VPN_MTU, virtualIP, virtualNetMask, localSocks, virtualGateway + ":" + pdnsdPort, localDnsTransparentProxy);
+					}
 
 				}
 		        catch (Exception e)
@@ -409,7 +417,7 @@ public class OrbotVpnManager implements Handler.Callback {
 
 		File fileConf = makePdnsdConf(mService, mService.getFilesDir(), torDnsHost, torDnsPort, pdnsdHost, pdnsdPort);
 
-        String[] cmdString = {pdnsPath,"-c",fileConf.toString()};
+        String[] cmdString = {pdnsPath,"-c",fileConf.toString(),"-g","-v2"};
         ProcessBuilder pb = new ProcessBuilder(cmdString);
         pb.redirectErrorStream(true);
 		Process proc = pb.start();
diff --git a/orbotservice/src/main/java/org/torproject/android/service/vpn/TorVpnService.java b/orbotservice/src/main/java/org/torproject/android/service/vpn/TorVpnService.java
index d394b5c8..cedd2528 100644
--- a/orbotservice/src/main/java/org/torproject/android/service/vpn/TorVpnService.java
+++ b/orbotservice/src/main/java/org/torproject/android/service/vpn/TorVpnService.java
@@ -22,8 +22,8 @@ public class TorVpnService extends VpnService {
 
     public static final String TAG = "TorVpnService";
 
-    private static final String ACTION_START = "start";
-    private static final String ACTION_STOP = "stop";
+    public static final String ACTION_START = "start";
+    public static final String ACTION_STOP = "stop";
 
     public static void start(Context context) {
         Intent intent = new Intent(context, TorVpnService.class);
diff --git a/orbotservice/src/main/res/drawable/ic_onion.xml b/orbotservice/src/main/res/drawable/ic_onion.xml
new file mode 100644
index 00000000..3047a149
--- /dev/null
+++ b/orbotservice/src/main/res/drawable/ic_onion.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="69.9dp"
+    android:height="107.3dp"
+    android:viewportWidth="69.9"
+    android:viewportHeight="107.3">
+  <path
+      android:pathData="M59.9,54.5c-3.4,-3.1 -7.7,-5.6 -12.1,-8.1c-2,-1.1 -8.1,-5.9 -6,-12.7l-3.8,-1.6c6,-9.3 13.8,-18.5 23.4,-27.1c-7.7,2.6 -14.5,6.6 -19.6,13.7c3,-6.3 7.9,-12.5 13.3,-18.8c-7.4,5.3 -13.8,11.3 -17.8,19.3L40.1,8c-4,7.2 -6.8,14.5 -7.9,21.8l-5.9,-2.4l-1,0.8c5.2,9.3 2.5,14.2 -0.1,15.9C20,47.6 12.5,52.1 8.7,56c-7.2,7.4 -9.3,14.4 -8.6,23.7c0.7,11.9 9.4,21.8 20.9,25.7c5.1,1.7 9.8,1.9 15,1.9c8.4,0 17,-2.2 23.3,-7.5c6.7,-5.5 10.6,-13.9 10.6,-22.5C69.9,68.6 66.3,60.3 59.9,54.5zM18.6,101.8C9.3,97.3 3.1,87.6 2.7,79.7C1.9,63.6 9.6,58.9 16.8,53c4,-3.3 9.6,-4.9 12.8,-10.8c0.6,-1.3 1,-4.1 0.2,-7.1c-0.3,-1 -1.8,-4.6 -2.4,-5.4l8.9,3.9l0,0l0,0c-0.2,4.2 -0.3,7.6 0.5,10.7c0.9,3.4 5.3,8.3 7.1,14c3.5,10.8 2.6,24.9 0.1,35.9c-0.4,1.8 -1.7,4 -3.3,6c0.6,-1.1 1.1,-2.2 1.4,-3.4c2.5,-8.9 3.6,-13 2.4,-22.8c-0.2,-1 -0.6,-4.2 -2.1,-7.7C40.3,61 37.1,56 36.7,54.9c-0.7,-1.7 -1.7,-8.9 -1.8,-13.8C35,45.3 35.3,53 36.4,56c0.3,1 3.2,5.5 5.3,11c1.4,3.8 1.7,7.3 2,8.3c1,4.5 -0.2,12.1 -1.8,19.3c-0.5,2.6 -1.9,5
 .6 -3.7,7.9c1,-1.4 1.8,-3.2 2.4,-5.3c1.2,-4.2 1.7,-9.6 1.6,-13c-0.1,-2 -1,-6.3 -2.5,-10.2c-0.9,-2.1 -2.2,-4.3 -3.1,-5.8c-1,-1.5 -1,-4.8 -1.4,-8.6c0.1,4.1 -0.3,6.2 0.7,9.1c0.6,1.7 2.8,4.1 3.4,6.4c0.9,3.1 1.8,6.5 1.7,8.6c0,2.4 -0.1,6.8 -1.2,11.6c-0.7,3.6 -2.3,6.7 -4.9,8.7c1.1,-1.4 1.7,-2.8 2,-4.2c0.4,-2.1 0.5,-4.1 0.7,-6.6c0.2,-2.1 0.1,-4.8 -0.5,-7.7c-0.8,-3.6 -2.1,-7.2 -2.7,-9.7c0.1,2.8 1.2,6.3 1.7,10c0.4,2.7 0.2,5.4 0.1,7.8c-0.1,2.8 -1,7.7 -2.2,10.1c-1.2,-0.5 -1.6,-1.2 -2.4,-2.2c-1,-1.3 -1.6,-2.7 -2.2,-4.3c-0.5,-1.2 -1,-2.6 -1.3,-4.1c-0.3,-2.4 -0.2,-6.1 2.5,-9.9c2.1,-3 2.5,-3.2 3.2,-6.7c-1,3.1 -1.7,3.4 -3.9,6c-2.5,2.9 -2.9,7.1 -2.9,10.5c0,1.4 0.6,3 1.1,4.5c0.6,1.6 1.2,3.2 2,4.4c0.6,1 1.4,1.7 2.1,2.2c-2.6,-0.7 -5.3,-1.7 -7,-3.1c-4.2,-3.6 -7.9,-9.7 -8.4,-15.1c-0.4,-4.4 3.6,-10.8 9.3,-14c4.8,-2.8 5.9,-5.9 6.9,-11c-1.4,4.4 -2.8,8.2 -7.4,10.5C19,75 15.6,80.8 15.9,86.4c0.5,7.1 3.3,12 9,15.9c1.3,0.9 3.1,1.8 5,2.5c-7.1,-1.7 -8,-2.7 -10.4,-5.5c0,-0.2 -0.6,-0.6 -0.6,-0.7c-3.2,-3.6 -7.2,-9.8 -
 8.6,-15.5c-0.5,-2 -1,-4.1 -0.4,-6.1c2.6,-9.4 8.3,-13 14,-16.9c1.4,-1 2.8,-1.9 4.1,-2.9c3.2,-2.5 4,-9 4.7,-12.7c-1.3,4.5 -2.7,10.1 -5.2,11.9c-1.3,1 -2.9,1.8 -4.2,2.7c-5.9,4 -11.8,7.8 -14.5,17.5c-0.6,2.5 -0.2,4.3 0.4,6.7c1.5,5.9 5.5,12.3 8.9,16.1c0,0 0.6,0.6 0.6,0.6c1.5,1.7 3.4,3 5.7,3.9C22.3,103.4 20.4,102.7 18.6,101.8z"
+      android:fillColor="#010101"/>
+</vector>
diff --git a/orbotservice/src/main/res/drawable/ic_stat_tor_xfer.png b/orbotservice/src/main/res/drawable/ic_stat_tor_xfer.png
new file mode 100644
index 00000000..d222fb67
Binary files /dev/null and b/orbotservice/src/main/res/drawable/ic_stat_tor_xfer.png differ
diff --git a/orbotservice/src/main/res/drawable/ic_transfer.xml b/orbotservice/src/main/res/drawable/ic_transfer.xml
new file mode 100644
index 00000000..a2d1fa99
--- /dev/null
+++ b/orbotservice/src/main/res/drawable/ic_transfer.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M9,3L5,6.99h3L8,14h2L10,6.99h3L9,3zM16,17.01L16,10h-2v7.01h-3L15,21l4,-3.99h-3z"/>
+</vector>





More information about the tor-commits mailing list