package com.eolwral.osmonitor.ui; import java.io.File; import java.io.FileWriter; import java.net.InetAddress; import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Locale; import org.json.JSONException; import org.json.JSONObject; import android.annotation.SuppressLint; import android.app.AlertDialog; import android.app.ProgressDialog; import android.app.AlertDialog.Builder; import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnCancelListener; import android.content.res.Resources; import android.content.Intent; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; import android.support.v4.app.ListFragment; import android.support.v4.content.LocalBroadcastManager; import android.support.v4.util.SimpleArrayMap; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.EditText; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; import com.android.volley.Request; import com.android.volley.Response.ErrorListener; import com.android.volley.Response.Listener; import com.android.volley.VolleyError; import com.android.volley.toolbox.JsonObjectRequest; import com.eolwral.osmonitor.R; import com.eolwral.osmonitor.core.connectionInfo; import com.eolwral.osmonitor.core.connectionInfoList; import com.eolwral.osmonitor.core.processInfo; import com.eolwral.osmonitor.core.processInfoList; import com.eolwral.osmonitor.ipc.IpcService; import com.eolwral.osmonitor.ipc.IpcService.ipcClientListener; import com.eolwral.osmonitor.ipc.ipcCategory; import com.eolwral.osmonitor.ipc.ipcData; import com.eolwral.osmonitor.ipc.ipcMessage; import com.eolwral.osmonitor.preference.OSMPreference; import com.eolwral.osmonitor.settings.Settings; import com.eolwral.osmonitor.util.ProcessUtil; import com.eolwral.osmonitor.util.HttpUtil; import com.eolwral.osmonitor.util.UserInterfaceUtil; public class ConnectionFragment extends ListFragment implements ipcClientListener { // ipc client private IpcService ipcService = IpcService.getInstance(); private boolean ipcStop = false; // data private ArrayList<connectionInfo> data = new ArrayList<connectionInfo>(); private Settings settings = null; private ProcessUtil infoHelper = null; @SuppressLint("UseSparseArrays") private SimpleArrayMap<Integer, String> map = new SimpleArrayMap<Integer, String>(); // tablet private boolean tabletLayout = false; private Fragment previousMap = null; private ProgressDialog procDialog = null; // stop or start private boolean stopUpdate = false; private MenuItem stopButton = null; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); settings = Settings.getInstance(getActivity().getApplicationContext()); infoHelper = ProcessUtil.getInstance(getActivity().getApplicationContext(), true); setListAdapter(new ConnectionListAdapter(getActivity() .getApplicationContext())); } public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater .inflate(R.layout.ui_connection_fragment, container, false); // detect layout if (v.findViewById(R.id.ui_connection_map) != null) tabletLayout = true; else tabletLayout = false; // enable fragment option menu setHasOptionsMenu(true); return v; } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.ui_connection_menu, menu); super.onCreateOptionsMenu(menu, inflater); // refresh button stopButton = (MenuItem) menu.findItem(R.id.ui_menu_stop); if (stopUpdate) stopButton.setIcon(R.drawable.ic_action_start); else stopButton.setIcon(R.drawable.ic_action_stop); return; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.ui_menu_setting: onSettingClick(); break; case R.id.ui_menu_stop: onStopClick(item); break; case R.id.ui_menu_exit: onExitClick(); break; case R.id.ui_connection_export: onExportClick(); break; } return super.onOptionsItemSelected(item); } private void onExportClick() { final Resources exportRes = getActivity().getResources(); final Calendar calendar = Calendar.getInstance(); final SimpleDateFormat formatter = new SimpleDateFormat( "yyyy-MM-dd-hh.mm.ss", Locale.getDefault()); Builder exportDialog = new AlertDialog.Builder(getActivity()); View exportView = LayoutInflater.from(getActivity()).inflate( R.layout.ui_message_export, null); TextView exportFile = (TextView) exportView .findViewById(R.id.id_export_filename); exportFile.setText("Connection-" + formatter.format(calendar.getTime())); exportDialog.setView(exportView); exportDialog.setTitle(exportRes.getText(R.string.ui_menu_logexport)); exportDialog.setNegativeButton(exportRes.getText(R.string.ui_text_cancel), null); exportDialog.setPositiveButton(exportRes.getText(R.string.ui_text_okay), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { String FileName = ((EditText) ((AlertDialog) dialog) .findViewById(R.id.id_export_filename)).getText().toString(); exportLog(FileName); } }); exportDialog.create().show(); return; } private void exportLog(String fileName) { if (fileName.trim().equals("")) return; if (!fileName.contains(".csv")) fileName += ".csv"; try { File logFile = new File(Environment.getExternalStorageDirectory() .getPath() + "/" + fileName); if (logFile.exists()) { new AlertDialog.Builder(getActivity()) .setTitle(R.string.ui_menu_logexport) .setMessage(R.string.ui_text_fileexist) .setPositiveButton(R.string.ui_text_okay, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { } }).create().show(); return; } logFile.createNewFile(); FileWriter logWriter = new FileWriter(logFile); for (int index = 0; index < data.size(); index++) { StringBuilder logLine = new StringBuilder(); // item connectionInfo item = data.get(index); // prepare main information logLine.append(UserInterfaceUtil.getConnectionType(item.type())); logLine.append(","); logLine.append(UserInterfaceUtil.convertToIPv4(item.localIP(), item.localPort())); logLine.append(","); logLine.append(UserInterfaceUtil.convertToIPv4(item.remoteIP(), item.remotePort())); logLine.append(","); logLine.append(UserInterfaceUtil.getConnectionStatus(item.status())); logLine.append(","); if (item.uid() == 0) logLine.append("System"); else if (map.containsKey(item.uid())) logLine.append(infoHelper.getPackageName(map.get(item.uid()))); else logLine.append(item.uid() + "(UID)"); logLine.append("\n"); logWriter.write(logLine.toString()); } logWriter.close(); } catch (Exception e) { new AlertDialog.Builder(getActivity()) .setTitle(R.string.ui_menu_logexport) .setMessage(e.getMessage()) .setPositiveButton(R.string.ui_text_okay, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { } }).create().show(); return; } new AlertDialog.Builder(getActivity()) .setTitle(R.string.ui_menu_logexport) .setMessage(R.string.ui_text_exportdone) .setPositiveButton(R.string.ui_text_okay, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { } }).create().show(); } private void onStopClick(MenuItem stopButton) { stopUpdate = !stopUpdate; if (stopUpdate) stopButton.setIcon(R.drawable.ic_action_start); else stopButton.setIcon(R.drawable.ic_action_stop); return; } private void onExitClick() { LocalBroadcastManager.getInstance(getActivity()).sendBroadcast( new Intent("Exit")); return; } private void onSettingClick() { Intent settings = new Intent(getActivity(), OSMPreference.class); settings.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(settings); return; } @SuppressLint("NewApi") @Override public void onListItemClick(ListView l, View v, int position, long id) { // detect local IP address via InetAddress try { InetAddress ia = InetAddress.getByName(data.get(position).remoteIP()); if (ia.isAnyLocalAddress() || ia.isSiteLocalAddress() || ia.isLoopbackAddress() ) return; } catch (Exception e) { } // execute WHOIS procedure String QueryIP = data.get(position).remoteIP().replace("::ffff:", ""); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) new PrepareQuery().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, QueryIP); else new PrepareQuery().execute(QueryIP); }; @Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); ipcService.removeRequest(this); ipcStop = !isVisibleToUser; if (isVisibleToUser == true) { byte newCommand[] = { ipcCategory.CONNECTION, ipcCategory.PROCESS }; ipcService.addRequest(newCommand, 0, this); } } @Override public void onRecvData(byte [] result) { // check if (ipcStop == true) return; if (stopUpdate == true || result == null) { byte newCommand[] = { ipcCategory.CONNECTION, ipcCategory.PROCESS }; ipcService.addRequest(newCommand, settings.getInterval(), this); return; } // clean up while (!data.isEmpty()) data.remove(0); data.clear(); map.clear(); // convert data ipcMessage resultMessage = ipcMessage.getRootAsipcMessage(ByteBuffer.wrap(result)); try { for (int index = 0; index < resultMessage.dataLength(); index++) { ipcData rawData = resultMessage.data(index); // prepare mapping table if (rawData.category() == ipcCategory.PROCESS) extractProfessInfo(rawData); else if (rawData.category() == ipcCategory.CONNECTION) extractConnectionInfo(rawData); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } ((ConnectionListAdapter) getListAdapter()).refresh(); // send command again byte newCommand[] = { ipcCategory.CONNECTION, ipcCategory.PROCESS }; ipcService.addRequest(newCommand, settings.getInterval(), this); } private void extractConnectionInfo(ipcData rawData) { // process connectionInfo connectionInfoList list = connectionInfoList.getRootAsconnectionInfoList(rawData.payloadAsByteBuffer().asReadOnlyBuffer()); for (int count = 0; count < list.listLength(); count++) { connectionInfo cnInfo = list.list(count); data.add(cnInfo); } } private void extractProfessInfo(ipcData rawData) { processInfoList list = processInfoList.getRootAsprocessInfoList(rawData.payloadAsByteBuffer().asReadOnlyBuffer()); for (int count = 0; count < list.listLength(); count++) { processInfo psInfo = list.list(count); if (!infoHelper.checkPackageInformation(psInfo.name())) { infoHelper.doCacheInfo(psInfo.uid(), psInfo.owner(), psInfo.name()); } map.put(psInfo.uid(), psInfo.name()); } } /** * implement viewholder class for connection list */ private class ViewHolder { // main information ImageView icon; TextView type; TextView src; TextView dst; TextView owner; TextView status; } private class ConnectionListAdapter extends BaseAdapter { private LayoutInflater itemInflater = null; private ViewHolder holder = null; public ConnectionListAdapter(Context mContext) { itemInflater = (LayoutInflater) mContext .getSystemService(Context.LAYOUT_INFLATER_SERVICE); } public int getCount() { return data.size(); } public Object getItem(int position) { return position; } public long getItemId(int position) { return position; } public View getView(int position, View convertView, ViewGroup parent) { View sv = null; // get data connectionInfo item = data.get(position); // prepare view if (convertView == null) { sv = (View) itemInflater.inflate(R.layout.ui_connection_item, parent, false); holder = new ViewHolder(); holder.icon = ((ImageView) sv.findViewById(R.id.id_connection_icon)); holder.type = ((TextView) sv.findViewById(R.id.id_connection_type)); holder.src = ((TextView) sv.findViewById(R.id.id_connection_src)); holder.dst = ((TextView) sv.findViewById(R.id.id_connection_dst)); holder.owner = ((TextView) sv.findViewById(R.id.id_connection_owner)); holder.status = ((TextView) sv.findViewById(R.id.id_connection_status)); sv.setTag(holder); } else { sv = (View) convertView; holder = (ViewHolder) sv.getTag(); } // draw current color for each item if (position % 2 == 0) sv.setBackgroundColor(getResources().getColor(R.color.dkgrey_osmonitor)); else sv.setBackgroundColor(getResources().getColor(R.color.black_osmonitor)); // draw icon when screen is not small if (holder.icon != null) { if (item.uid() == 0) holder.icon.setImageDrawable(infoHelper.getDefaultIcon()); else holder.icon.setImageDrawable(infoHelper.getPackageIcon(map.get(item.uid()))); } // prepare main information holder.type.setText(UserInterfaceUtil.getConnectionType(item.type())); holder.src.setText(UserInterfaceUtil.convertToIPv4(item.localIP(), item.localPort())); holder.dst.setText(UserInterfaceUtil.convertToIPv4(item.remoteIP(), item.remotePort())); holder.status.setText(UserInterfaceUtil.getConnectionStatus(item.status())); if (item.uid() == 0) holder.owner.setText("System"); else if (map.containsKey(item.uid())) holder.owner.setText(infoHelper.getPackageName(map.get(item.uid()))); else holder.owner.setText(item.uid() + "(UID)"); return sv; } public void refresh() { this.notifyDataSetChanged(); } } private void showLoading() { getActivity().runOnUiThread(new Runnable() { public void run() { // show progress dialog procDialog = ProgressDialog.show(getActivity(), "", getActivity() .getResources().getText(R.string.ui_text_refresh), true); procDialog.setOnCancelListener(new OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { HttpUtil.getInstance(getActivity().getApplicationContext()) .cancelRequest(); } }); procDialog.setCancelable(true); } }); } private void closeLoading() { // because activity may be destroyed by system, we need check it before // using. // Fix: java.lang.NullPointerException if (getActivity() == null) return; getActivity().runOnUiThread(new Runnable() { public void run() { if (procDialog != null) procDialog.dismiss(); procDialog = null; } }); } private void showMap(CacheQuery result) { // Replace One Fragment with Another // http://developer.android.com/training/basics/fragments/fragment-ui.html#Replace // pass information ConnectionStaticMapFragment newMap = new ConnectionStaticMapFragment(); Bundle args = new Bundle(); args.putFloat(ConnectionStaticMapFragment.LONGTIUDE, result.Longtiude); args.putFloat(ConnectionStaticMapFragment.LATITUDE, result.Latitude); args.putString(ConnectionStaticMapFragment.MESSAGE, result.Msg); newMap.setArguments(args); // if Activity isn't attached, just skip if (getActivity() == null) return; final FragmentManager fm = getActivity().getSupportFragmentManager(); final FragmentTransaction transaction = fm.beginTransaction(); if (tabletLayout) { // push current fragment transaction.replace(R.id.ui_connection_map, newMap, "WHOIS"); transaction.commitAllowingStateLoss(); previousMap = newMap; } else { // replace current fragment transaction.replace(R.id.ui_connection_layout, newMap, "WHOIS"); transaction.addToBackStack(null); transaction.commitAllowingStateLoss(); } } private void cleanUp() { final FragmentTransaction transaction = getActivity() .getSupportFragmentManager().beginTransaction(); if (previousMap != null) { transaction.remove(previousMap).commit(); previousMap = null; } } class CacheQuery { public String Msg; public float Longtiude; public float Latitude; } private final SimpleArrayMap<String, CacheQuery> CacheWhois = new SimpleArrayMap<String, CacheQuery>(); class PrepareQuery extends AsyncTask<String, Void, Void> { @Override protected Void doInBackground(String... params) { String QueryIP = params[0]; if (QueryIP != null) new QueryWhois(QueryIP); return null; } } class QueryWhois { public QueryWhois(String QueryIP) { // if data cached, showMap directly if (CacheWhois.get(QueryIP) != null) { showMap(CacheWhois.get(QueryIP)); return; } // clean up cleanUp(); // show prepare dialog showLoading(); String URL = "https://osmonitor.mobi/WhoisAPI?IP=" + QueryIP; JsonObjectRequest queryRequest = new JsonObjectRequest( Request.Method.GET, URL, null, new Response(QueryIP), new ResponseError()); HttpUtil.getInstance(getActivity().getApplicationContext()).addRequest( queryRequest); } private String getHostName(String QueryIP) { // nslookup String HostName = QueryIP; try { HostName = InetAddress.getByName(QueryIP).getHostName(); } catch (UnknownHostException e) { } return HostName; } private class Response implements Listener<JSONObject> { private String QueryIP; private String HostName; public Response(String QueryIP) { this.QueryIP = QueryIP; this.HostName = getHostName(QueryIP); } @Override public void onResponse(JSONObject response) { CacheQuery WhoisQuery = new CacheQuery(); StringBuilder whoisInfo = new StringBuilder(); whoisInfo.append("<b>DNS:</b> " + HostName + "<br/>"); whoisInfo.append("<b>IP:</b> " + QueryIP + "<br/>"); try { whoisInfo.append("<b>Country:</b> " + response.getString("country") + "<br/>"); whoisInfo.append("<b>Region:</b> " + response.getString("regionName") + "<br/>"); whoisInfo.append("<b>City:</b> " + response.getString("city") + "<br/>"); whoisInfo.append("<b>ISP:</b> " + response.getString("isp") + "<br/>"); whoisInfo.append("<b>Org:</b> " + response.getString("org") + "<br/>"); whoisInfo.append("<b>Latitude:</b> " + response.getString("lat") + "<br/>"); whoisInfo.append("<b>Longitude:</b> " + response.getString("lon")); WhoisQuery.Longtiude = (float) response.getDouble("lon"); WhoisQuery.Latitude = (float) response.getDouble("lat"); } catch (JSONException e) { } WhoisQuery.Msg = whoisInfo.toString(); CacheWhois.put(QueryIP, WhoisQuery); closeLoading(); showMap(WhoisQuery); } } private class ResponseError implements ErrorListener { @Override public void onErrorResponse(VolleyError error) { closeLoading(); } } } }