com.googlecode.networklog.NetworkLog.java Source code

Java tutorial

Introduction

Here is the source code for com.googlecode.networklog.NetworkLog.java

Source

/* (C) 2012 Pragmatic Software
   This Source Code Form is subject to the terms of the Mozilla Public
   License, v. 2.0. If a copy of the MPL was not distributed with this
   file, You can obtain one at http://mozilla.org/MPL/2.0/
 */

package com.googlecode.networklog;

import android.os.Bundle;
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.content.ServiceConnection;
import android.content.ComponentName;
import android.content.res.Resources;
import android.os.Messenger;
import android.os.RemoteException;
import android.os.IBinder;
import android.content.Context;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.widget.TextView;
import android.widget.ToggleButton;
import android.widget.CheckBox;
import android.view.View;
import android.view.LayoutInflater;
import android.util.Log;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningServiceInfo;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;

import com.viewpagerindicator.TitlePageIndicator;
import com.viewpagerindicator.TitleProvider;

import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.app.ActionBar;

import java.util.Enumeration;
import java.net.NetworkInterface;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.ArrayList;
import java.io.File;

public class NetworkLog extends SherlockFragmentActivity {
    public static final String SCRIPT = "networklog.sh";

    public static RetainInstanceData data = null;

    public static ViewPager viewPager;
    public final static int PAGE_LOG = 0;
    public final static int PAGE_APP = 1;
    public final static int PAGES = 2;

    public static LogFragment logFragment;
    public static AppFragment appFragment;

    public static ToggleButton loggingButton;
    public static TextView statusText;
    public static Settings settings;
    public static Handler handler;

    public static String filterTextInclude;
    public static ArrayList<String> filterTextIncludeList = new ArrayList<String>();
    public static boolean filterUidInclude;
    public static boolean filterNameInclude;
    public static boolean filterAddressInclude;
    public static boolean filterPortInclude;
    public static boolean filterInterfaceInclude;
    public static boolean filterProtocolInclude;

    public static String filterTextExclude;
    public static ArrayList<String> filterTextExcludeList = new ArrayList<String>();
    public static boolean filterUidExclude;
    public static boolean filterNameExclude;
    public static boolean filterAddressExclude;
    public static boolean filterPortExclude;
    public static boolean filterInterfaceExclude;
    public static boolean filterProtocolExclude;

    public static NetworkResolver resolver;
    public static boolean resolveHosts;
    public static boolean resolvePorts;
    public static boolean resolveCopies;

    public static boolean startServiceAtStart;
    public static boolean stopServiceAtExit;

    public static HistoryLoader history;
    public static FeedbackDialog feedbackDialog;
    public static ExportDialog exportDialog;
    public static ClearLog clearLog;
    public static SelectBlockedApps selectBlockedApps;
    public static SelectToastApps selectToastApps;

    public static StatusUpdater statusUpdater;

    public static ArrayList<String> localIpAddrs;

    public static Messenger service = null;
    public static Messenger messenger = null;
    public static boolean isBound = false;

    public static Context context;
    public static Menu menu;

    public static NetworkLog instance;

    public static ServiceConnection connection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder serv) {
            service = new Messenger(serv);
            isBound = true;

            MyLog.d("Attached to service; setting isBound true");

            // Register with service
            try {
                Message msg = Message.obtain(null, NetworkLogService.MSG_REGISTER_CLIENT);
                msg.replyTo = messenger;
                service.send(msg);
            } catch (RemoteException e) {
                /* do nothing */
                Log.d("NetworkLog", "RemoteException registering with service", e);
            }
        }

        public void onServiceDisconnected(ComponentName className) {
            MyLog.d("Detached from service; setting isBound false");
            service = null;
            isBound = false;
        }
    };

    private LogEntry entry;

    class IncomingHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            if (MyLog.enabled && MyLog.level >= 2) {
                MyLog.d(2, "[client] Received message: " + msg);
            }

            switch (msg.what) {
            case NetworkLogService.MSG_BROADCAST_LOG_ENTRY:
                entry = (LogEntry) msg.obj;
                if (MyLog.enabled && MyLog.level >= 2) {
                    MyLog.d(2, "Received entry: " + entry);
                }
                logFragment.onNewLogEntry(entry);
                appFragment.onNewLogEntry(entry);
                break;

            default:
                super.handleMessage(msg);
            }
        }
    }

    void doBindService() {
        MyLog.d("doBindService");
        if (isBound) {
            MyLog.d("Already bound to service; unbinding...");
            doUnbindService();
        }

        messenger = new Messenger(new IncomingHandler());
        MyLog.d("Created messenger: " + messenger);

        MyLog.d("Binding connection to service: " + connection);
        bindService(new Intent(this, NetworkLogService.class), connection, 0);
        MyLog.d("doBindService done");
    }

    void doUnbindService() {
        MyLog.d("doUnbindService");
        if (isBound) {
            if (service != null) {
                try {
                    MyLog.d("Unregistering from service; service: " + service + "; messenger: " + messenger);
                    Message msg = Message.obtain(null, NetworkLogService.MSG_UNREGISTER_CLIENT);
                    msg.replyTo = messenger;
                    service.send(msg);
                } catch (RemoteException e) {
                    /* do nothing */
                    Log.d("NetworkLog", "RemoteException unregistering with service", e);
                }

                try {
                    MyLog.d("Unbinding connection from service:" + connection);
                    unbindService(connection);
                } catch (Exception e) {
                    Log.d("NetworkLog", "Ignored unbind exception:", e);
                } finally {
                    MyLog.d("Setting isBound false");
                    isBound = false;
                }

                MyLog.d("doUnbindService done");
            }
        }
    }

    public static State state;

    public enum State {
        LOAD_APPS, RUNNING, EXITING
    };

    public static InitRunner initRunner;

    public class InitRunner implements Runnable {
        private Context context;
        public boolean running = false;

        public InitRunner(Context context) {
            this.context = context;
        }

        public void stop() {
            running = false;
        }

        public void run() {
            MyLog.d("Init begin");
            running = true;

            Looper.myLooper().prepare();

            state = NetworkLog.State.LOAD_APPS;
            ApplicationsTracker.getInstalledApps(context, handler);

            if (running == false) {
                return;
            }

            history.loadEntriesFromFile(context, settings.getHistorySize());

            if (startServiceAtStart && !isServiceRunning(context, NetworkLogService.class.getName())) {
                handler.post(new Runnable() {
                    public void run() {
                        startService();
                    }
                });
            }

            state = NetworkLog.State.RUNNING;
            MyLog.d("Init done");
        }
    }

    public void loadSettings() {
        if (settings == null) {
            settings = new Settings(this);
        }

        filterTextInclude = settings.getFilterTextInclude();
        FilterUtils.buildList(filterTextInclude, filterTextIncludeList);
        filterUidInclude = settings.getFilterUidInclude();
        filterNameInclude = settings.getFilterNameInclude();
        filterAddressInclude = settings.getFilterAddressInclude();
        filterPortInclude = settings.getFilterPortInclude();
        filterInterfaceInclude = settings.getFilterInterfaceInclude();
        filterProtocolInclude = settings.getFilterProtocolInclude();

        filterTextExclude = settings.getFilterTextExclude();
        FilterUtils.buildList(filterTextExclude, filterTextExcludeList);
        filterUidExclude = settings.getFilterUidExclude();
        filterNameExclude = settings.getFilterNameExclude();
        filterAddressExclude = settings.getFilterAddressExclude();
        filterPortExclude = settings.getFilterPortExclude();
        filterInterfaceExclude = settings.getFilterInterfaceExclude();
        filterProtocolExclude = settings.getFilterProtocolExclude();

        startServiceAtStart = settings.getStartServiceAtStart();
        stopServiceAtExit = settings.getStopServiceAtExit();

        resolveHosts = settings.getResolveHosts();
        resolvePorts = settings.getResolvePorts();
        resolveCopies = settings.getResolveCopies();

        startServiceAtStart = settings.getStartServiceAtStart();
        stopServiceAtExit = settings.getStopServiceAtExit();

        NetworkLogService.throughputBps = settings.getThroughputBps();
        NetworkLogService.toastEnabled = settings.getToastNotifications();
        NetworkLogService.toastDuration = settings.getToastNotificationsDuration();
        NetworkLogService.toastPosition = settings.getToastNotificationsPosition();
        NetworkLogService.toastYOffset = settings.getToastNotificationsYOffset();
        NetworkLogService.toastOpacity = settings.getToastNotificationsOpacity();
        NetworkLogService.toastShowAddress = settings.getToastNotificationsShowAddress();

        NetworkLogService.blockedApps = new SelectBlockedApps().loadBlockedApps(this);
        NetworkLogService.toastBlockedApps = new SelectToastApps().loadBlockedApps(this);
    }

    private static class MyFragmentPagerAdapter extends FragmentPagerAdapter implements TitleProvider {
        Context context;

        public MyFragmentPagerAdapter(Context context, FragmentManager fm) {
            super(fm);
            this.context = context;
        }

        @Override
        public String getTitle(int index) {
            switch (index) {
            case PAGE_LOG:
                return context.getResources().getString(R.string.tab_log);
            case PAGE_APP:
                return context.getResources().getString(R.string.tab_apps);
            }
            return "Unnamed";
        }

        @Override
        public Fragment getItem(int index) {
            Fragment fragment = null;

            switch (index) {
            case PAGE_LOG:
                fragment = logFragment;
                break;
            case PAGE_APP:
                fragment = appFragment;
                break;
            }

            return fragment;
        }

        @Override
        public int getCount() {
            return PAGES;
        }
    }

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        MyLog.d("NetworkLog started");

        context = this;
        instance = this;

        loadSettings();
        getLocalIpAddresses();

        data = (RetainInstanceData) getLastCustomNonConfigurationInstance();

        if (data != null) {
            MyLog.d("Restored run");
            ApplicationsTracker.restoreData(data);
            resolver = data.networkLogResolver;

            // restore history loading progress dialog
            history.dialog_showing = data.historyDialogShowing;
            history.dialog_max = data.historyDialogMax;
            history.dialog_progress = data.historyDialogProgress;

            if (history.dialog_showing) {
                history.createProgressDialog(this);
            }

            if (data.feedbackDialogMessage != null) {
                feedbackDialog = new FeedbackDialog(this);
                feedbackDialog.setMessage(data.feedbackDialogMessage);
                feedbackDialog.setAttachLogcat(data.feedbackDialogAttachLogcat);
                feedbackDialog.setCursorPosition(data.feedbackDialogCursorPosition);
                feedbackDialog.show();
            }

            if (data.clearLogDialogShowing) {
                clearLog.showClearLogDialog(this);
            }

            clearLog.progress = data.clearLogProgress;
            clearLog.progress_max = data.clearLogProgressMax;

            if (data.clearLogProgressDialogShowing) {
                clearLog.showProgressDialog(this);
            }

            if (data.exportDialogShowing) {
                exportDialog = new ExportDialog(this);
                exportDialog.setStartDate(data.exportDialogStartDate);
                exportDialog.setEndDate(data.exportDialogEndDate);
                exportDialog.setFile(data.exportDialogFile);
                exportDialog.show();
                exportDialog.datePickerMode = data.exportDialogDatePickerMode;
                exportDialog.restoreDatePickerListener();
            }

            if (data.exportDialogProgressDialogShowing) {
                exportDialog.progress = data.exportDialogProgress;
                exportDialog.progress_max = data.exportDialogProgressMax;
                exportDialog.showProgressDialog(this);
            }
        } else {
            MyLog.d("Fresh run");
            resolver = new NetworkResolver();

            logFragment = (LogFragment) Fragment.instantiate(this, LogFragment.class.getName());
            appFragment = (AppFragment) Fragment.instantiate(this, AppFragment.class.getName());
        }

        logFragment.setParent(this);
        appFragment.setParent(this);

        handler = new Handler();

        setContentView(R.layout.main);

        ActionBar actionBar = getSupportActionBar();
        actionBar.setCustomView(R.layout.actionbar_top);
        actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_SHOW_CUSTOM);

        if (history == null) {
            history = new HistoryLoader();
        }

        if (clearLog == null) {
            clearLog = new ClearLog();
        }

        statusText = (TextView) findViewById(R.id.statusText);

        viewPager = (ViewPager) findViewById(R.id.viewpager);
        MyFragmentPagerAdapter pagerAdapter = new MyFragmentPagerAdapter(this, getSupportFragmentManager());

        viewPager.setAdapter(pagerAdapter);

        TitlePageIndicator titleIndicator = (TitlePageIndicator) findViewById(R.id.titles);
        titleIndicator.setViewPager(viewPager);

        viewPager.setCurrentItem(1);

        if (isServiceRunning(this, NetworkLogService.class.getName())) {
            doBindService();
        }

        if (data == null) {
            initRunner = new InitRunner(this);
            new Thread(initRunner, "Initialization " + initRunner).start();
        } else {
            state = data.networkLogState;

            if (state != NetworkLog.State.RUNNING) {
                initRunner = new InitRunner(this);
                new Thread(initRunner, "Initialization " + initRunner).start();
            }

            // all data should be restored at this point, release the object
            data = null;
            MyLog.d("data object released");

            state = NetworkLog.State.RUNNING;
        }
        statusUpdater = new StatusUpdater();
        new Thread(statusUpdater, "StatusUpdater").start();
        ThroughputTracker.updateThroughput(0, 0);
    }

    @Override
    public void onResume() {
        super.onResume();
        MyLog.d("NetworkLog onResume");
    }

    @Override
    public void onPause() {
        super.onPause();
        MyLog.d("NetworkLog onPause");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        MyLog.d("NetworkLog onDestroy");

        if (data == null) {
            // exiting
            state = NetworkLog.State.EXITING;
            if (stopServiceAtExit) {
                stopService();
                ApplicationsTracker.stopWatchingPackages();
            } else if (NetworkLogService.instance == null) {
                ApplicationsTracker.stopWatchingPackages();
            }
        } else {
            // changing configuration
        }

        if (history.dialog_showing == true && history.dialog != null) {
            history.dialog.dismiss();
            history.dialog = null;
        }

        if (feedbackDialog != null && feedbackDialog.dialog != null && feedbackDialog.dialog.isShowing()) {
            feedbackDialog.dismiss();
            feedbackDialog = null;
        }

        if (exportDialog != null && exportDialog.dialog != null && exportDialog.dialog.isShowing()) {
            exportDialog.dismiss();
            exportDialog = null;
        }

        if (clearLog.dialog != null && clearLog.dialog.isShowing()) {
            clearLog.dialog.dismiss();
            clearLog.dialog = null;
        }

        if (clearLog.progressDialog != null && clearLog.progressDialog.isShowing()) {
            clearLog.progressDialog.dismiss();
            clearLog.progressDialog = null;
        }

        if (initRunner != null) {
            initRunner.stop();
        }

        if (logFragment != null) {
            logFragment.stopUpdater();
        }

        if (appFragment != null) {
            appFragment.stopUpdater();
        }

        if (statusUpdater != null) {
            statusUpdater.stop();
        }

        synchronized (ApplicationsTracker.dialogLock) {
            if (ApplicationsTracker.dialog != null) {
                ApplicationsTracker.dialog.dismiss();
                ApplicationsTracker.dialog = null;
            }
        }

        if (isBound) {
            doUnbindService();
        }

        instance = null;
    }

    @Override
    public Object onRetainCustomNonConfigurationInstance() {
        MyLog.d("Saving run");
        data = new RetainInstanceData();
        return data;
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getSupportMenuInflater();
        inflater.inflate(R.layout.menu, menu);
        return true;
    }

    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        this.menu = menu;
        MenuItem item = menu.findItem(R.id.sort);

        if (viewPager.getCurrentItem() == PAGE_APP) {
            item.setVisible(true);

            Sort sortBy;
            if (appFragment == null || appFragment.sortBy == null) {
                sortBy = NetworkLog.settings.getSortBy();
            } else {
                sortBy = appFragment.sortBy;
            }

            switch (sortBy) {
            case UID:
                item = menu.findItem(R.id.sort_by_uid);
                break;
            case NAME:
                item = menu.findItem(R.id.sort_by_name);
                break;
            case THROUGHPUT:
                item = menu.findItem(R.id.sort_by_throughput);
                break;
            case PACKETS:
                item = menu.findItem(R.id.sort_by_packets);
                break;
            case BYTES:
                item = menu.findItem(R.id.sort_by_bytes);
                break;
            case TIMESTAMP:
                item = menu.findItem(R.id.sort_by_timestamp);
                break;
            default:
                NetworkLog.settings.setSortBy(Sort.BYTES);
                item = menu.findItem(R.id.sort_by_bytes);
            }

            item.setChecked(true);
        } else {
            item.setVisible(false);
        }

        loggingButton = (ToggleButton) findViewById(R.id.actionbar_service_toggle);

        if (isServiceRunning(this, NetworkLogService.class.getName())) {
            loggingButton.setChecked(true);
        } else {
            loggingButton.setChecked(false);
        }

        return true;
    }

    public void serviceToggle(View view) {
        loggingButton = (ToggleButton) view;

        if (!isServiceRunning(this, NetworkLogService.class.getName())) {
            startService();
            loggingButton.setChecked(true);
        } else {
            stopService();
            loggingButton.setChecked(false);
        }
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.filter:
            showFilterDialog();
            break;
        case R.id.overallgraph:
            startActivity(new Intent(this, OverallAppTimelineGraph.class));
            break;
        case R.id.exit:
            finish();
            break;
        case R.id.feedback:
            feedbackDialog = new FeedbackDialog(this);
            feedbackDialog.show();
            break;
        case R.id.export:
            exportDialog = new ExportDialog(this);
            exportDialog.show();
            break;
        case R.id.clearlog:
            clearLog.showClearLogDialog(this);
            break;
        case R.id.settings:
            startActivity(new Intent(this, Preferences.class));
            break;
        case R.id.sort_by_uid:
            NetworkLog.settings.setSortBy(Sort.UID);
            item.setChecked(true);
            break;
        case R.id.sort_by_name:
            NetworkLog.settings.setSortBy(Sort.NAME);
            item.setChecked(true);
            break;
        case R.id.sort_by_throughput:
            NetworkLog.settings.setSortBy(Sort.THROUGHPUT);
            item.setChecked(true);
            break;
        case R.id.sort_by_packets:
            NetworkLog.settings.setSortBy(Sort.PACKETS);
            item.setChecked(true);
            break;
        case R.id.sort_by_bytes:
            NetworkLog.settings.setSortBy(Sort.BYTES);
            item.setChecked(true);
            break;
        case R.id.sort_by_timestamp:
            NetworkLog.settings.setSortBy(Sort.TIMESTAMP);
            item.setChecked(true);
            break;
        default:
            return super.onOptionsItemSelected(item);
        }

        return true;
    }

    @Override
    public void onBackPressed() {
        confirmExit();
    }

    public void showFilterDialog() {
        new FilterDialog(this);
    }

    public void confirmExit() {
        Context context = this;

        if (settings.getConfirmExit() == false) {
            finish();
            return;
        }

        StringBuilder message = new StringBuilder(getString(R.string.confirm_exit_text));
        boolean serviceRunning = isServiceRunning(context, NetworkLogService.class.getName());

        if (serviceRunning) {
            message.append("\n\n");
            if (stopServiceAtExit) {
                message.append(getString(R.string.logging_will_stop));
            } else {
                message.append(getString(R.string.logging_will_continue));
            }
        }

        View checkBoxView = View.inflate(this, R.layout.confirm_exit_checkbox, null);
        final CheckBox checkBox = (CheckBox) checkBoxView.findViewById(R.id.confirm_exit_checkbox);
        checkBox.setChecked(false);

        AlertDialog.Builder builder = new AlertDialog.Builder(context);
        builder.setTitle(getString(R.string.confirm_exit_title)).setMessage(message.toString()).setCancelable(true)
                .setView(checkBoxView)
                .setPositiveButton(getString(R.string.yes), new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        settings.setConfirmExit(!checkBox.isChecked());
                        finish();
                    }
                }).setNegativeButton(getString(R.string.no), new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        dialog.cancel();
                    }
                });
        AlertDialog alert = builder.create();
        alert.show();
    }

    public static void getLocalIpAddresses() {
        MyLog.d("getLocalIpAddresses");
        localIpAddrs = new ArrayList<String>();

        try {
            for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en
                    .hasMoreElements();) {
                NetworkInterface intf = en.nextElement();
                MyLog.d("Network interface found: " + intf.toString());

                for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
                    InetAddress inetAddress = enumIpAddr.nextElement();
                    MyLog.d("InetAddress: " + inetAddress.toString());

                    if (!inetAddress.isLoopbackAddress()) {
                        MyLog.d("Adding local IP address: [" + inetAddress.getHostAddress().toString() + "]");
                        localIpAddrs.add(inetAddress.getHostAddress().toString());
                    }
                }
            }
        } catch (SocketException ex) {
            Log.e("NetworkLog", ex.toString());
        }
    }

    public void startService() {
        MyLog.d("Starting service...");

        Intent intent = new Intent(this, NetworkLogService.class);

        intent.putExtra("logfile", settings.getLogFile());

        startService(intent);
        doBindService();
        updateStatusText();
    }

    public void stopService() {
        MyLog.d("Stopping service...");
        doUnbindService();
        stopService(new Intent(this, NetworkLogService.class));
        updateStatusText();
    }

    public static boolean isServiceRunning(Context context, String serviceName) {
        ActivityManager manager = (ActivityManager) context.getSystemService(ACTIVITY_SERVICE);

        for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
            if (MyLog.enabled) {
                MyLog.d("Service: " + service.service.getClassName() + "; " + service.pid + "; "
                        + service.clientCount + "; " + service.foreground + "; " + service.process);
            }

            if (serviceName.equals(service.service.getClassName())) {
                return true;
            }
        }

        return false;
    }

    public static void toggleServiceForeground(Boolean value) {
        MyLog.d("toggleServiceForeground " + value);

        if (isBound && service != null) {
            try {
                Message msg = Message.obtain(null, NetworkLogService.MSG_TOGGLE_FOREGROUND);
                msg.obj = value;
                service.send(msg);
            } catch (RemoteException e) {
                /* do nothing */
                Log.d("NetworkLog", "RemoteException toggling foreground", e);
            }
        }
    }

    static Runnable updateStatusRunner = new Runnable() {
        public void run() {
            updateStatusText();
        }
    };

    public static void updateStatus(int icon) {
        if (handler != null) {
            handler.post(updateStatusRunner);
        }
    }

    public static void updateStatusText() {
        if (context == null || statusText == null) {
            return;
        }

        StringBuilder sb = new StringBuilder();
        Resources res = context.getResources();

        if (filterTextInclude.length() > 0 || filterTextExclude.length() > 0) {
            sb.append(res.getString(R.string.filter_applied));

            if (filterTextInclude.length() > 0) {
                sb.append("+[" + filterTextInclude + "] ");
            }

            if (filterTextExclude.length() > 0) {
                sb.append("-[" + filterTextExclude + "] ");
            }
        }

        boolean serviceRunning = NetworkLogService.instance != null;

        if (!serviceRunning) {
            sb.append(res.getString(R.string.logging_inactive));
        }

        if (NetworkLogService.logfileString.length() > 0) {
            sb.append(context.getResources().getString(R.string.logfile_size));
            sb.append(NetworkLogService.logfileString);
        }

        if (serviceRunning && ThroughputTracker.throughputString.length() > 0) {
            sb.append(context.getResources().getString(R.string.throughput));
            sb.append(ThroughputTracker.throughputString);
        }

        statusText.setText(sb);
    }

    public static void updateNotificationText(String text) {
        if (isBound) {
            if (service != null) {
                try {
                    Message msg = Message.obtain(null, NetworkLogService.MSG_UPDATE_NOTIFICATION);
                    msg.obj = text;
                    service.send(msg);
                } catch (RemoteException e) {
                    /* do nothing */
                    Log.d("NetworkLog", "RemoteException updating notification", e);
                }
            }
        }
    }

    class StatusUpdater implements Runnable {
        boolean running = false;
        Runnable runner = new Runnable() {
            public void run() {
                MyLog.d(2, "Updating statusText");
                NetworkLogService.updateLogfileString();
                updateStatusText();
            }
        };

        public void stop() {
            running = false;
        }

        public void run() {
            running = true;
            MyLog.d("Starting status updater " + this);

            while (running) {
                runOnUiThread(runner);

                try {
                    Thread.sleep(15000);
                } catch (Exception e) {
                    Log.d("NetworkLog", "StatusUpdater", e);
                }
            }
            MyLog.d("Stopped status updater " + this);
        }
    }
}