├── .gitignore ├── .idea ├── .name ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── License.txt ├── README.md ├── app ├── .gitignore ├── build.gradle ├── google-services.json ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── edu │ │ └── buffalo │ │ └── cse │ │ └── ubwins │ │ └── cellmon │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── edu │ │ │ └── buffalo │ │ │ └── cse │ │ │ └── ubwins │ │ │ └── cellmon │ │ │ ├── Alarm.java │ │ │ ├── CellularDataRecorder.java │ │ │ ├── Cluster.java │ │ │ ├── Coordinate.java │ │ │ ├── DBHandler.java │ │ │ ├── DBstore.java │ │ │ ├── DatePickerFragment.java │ │ │ ├── DateSelectedListener.java │ │ │ ├── Entry.java │ │ │ ├── ForegroundService.java │ │ │ ├── HomeFragment.java │ │ │ ├── LocationFinder.java │ │ │ ├── MainActivity.java │ │ │ ├── MapFragment.java │ │ │ ├── MonthPickerFragment.java │ │ │ ├── NetworkStateReceiver.java │ │ │ ├── PhoneCallState.java │ │ │ ├── PhoneCallStateRecorder.java │ │ │ ├── ScheduleIntentReceiver.java │ │ │ ├── Scheduler.java │ │ │ ├── StartMyServiceAtBootReceiver.java │ │ │ └── WeekPickerFragment.java │ ├── proto │ │ ├── data_record.proto │ │ └── register_device.proto │ └── res │ │ ├── anim │ │ ├── move_left_in_activity.xml │ │ ├── move_left_out_activity.xml │ │ ├── move_right_in_activity.xml │ │ └── move_right_out_activity.xml │ │ ├── drawable-hdpi │ │ ├── drawer_shadow.9.png │ │ ├── ic_date_range_black_24dp.png │ │ ├── ic_date_range_white_24dp.png │ │ └── ic_drawer.png │ │ ├── drawable-mdpi │ │ ├── drawer_shadow.9.png │ │ ├── ic_date_range_black_24dp.png │ │ ├── ic_date_range_white_24dp.png │ │ └── ic_drawer.png │ │ ├── drawable-xhdpi │ │ ├── drawer_shadow.9.png │ │ ├── ic_date_range_black_24dp.png │ │ ├── ic_date_range_white_24dp.png │ │ └── ic_drawer.png │ │ ├── drawable-xxhdpi │ │ ├── ic_date_range_black_24dp.png │ │ └── ic_date_range_white_24dp.png │ │ ├── drawable-xxxhdpi │ │ ├── ic_date_range_black_24dp.png │ │ └── ic_date_range_white_24dp.png │ │ ├── drawable │ │ └── side_nav_bar.xml │ │ ├── layout │ │ ├── activity_base.xml │ │ ├── activity_main.xml │ │ ├── fragment_pick_week.xml │ │ ├── location_fragment.xml │ │ ├── nav_header_main.xml │ │ └── simple_list_item.xml │ │ ├── menu │ │ ├── app_menu.xml │ │ ├── home_menu.xml │ │ └── map_menu.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── m.png │ │ ├── mipmap-mdpi │ │ ├── cse50.png │ │ ├── ic_action_about.png │ │ ├── ic_action_computer.png │ │ ├── ic_action_place.png │ │ ├── ic_action_time.png │ │ ├── ic_globe.png │ │ ├── ic_launcher.png │ │ └── m.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── m.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── m.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── m.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ ├── protobuf_autogen │ └── debug │ │ └── java │ │ └── edu │ │ └── buffalo │ │ └── cse │ │ └── ubwins │ │ └── cellmon │ │ ├── DataRecordOuterClass.java │ │ └── RegisterDeviceOuterClass.java │ └── test │ └── java │ └── edu │ └── buffalo │ └── cse │ └── ubwins │ └── cellmon │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | CellularNetworkMonitor -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /License.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Abhishek Gautam, Armaan Goyal and UB Computer Science. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CellularNetworkMonitor 2 | 3 | [![License](https://img.shields.io/badge/license-MIT-blue.svg)](License.txt) 4 | 5 | An Android application that registers your device on to a Django based server keeping IMEI, 6 | carrier service, model make and Android Version as phone identifiers using a HTTP POST. 7 | Serialization of data sent over the network is achieved using [Google Protocol Buffers](https://github.com/google/protobuf) 8 | 9 | # Features: 10 | 1. Tracks location based on NETWORK_PROVIDER (strict and relaxed) and Fused API 11 | 2. Monitors [RSSI](https://en.wikipedia.org/wiki/Received_signal_strength_indication) and DBM levels 12 | 3. Current network state and other network parameters such as MCC, MNC, LAC and CID 13 | 4. Data activity 14 | 5. Stores all data in SQLite DB on local device storage 15 | 6. Reports can be exported in the form of CSV files 16 | 17 | # More to come: 18 | 7. Statistical Analysis in the form of UI 19 | 8. Map UI integration lets you know in what areas you get what Network Reception(both RSSI and data type) 20 | 21 | # UI: 22 | Check out [CellMon-UI](https://github.com/gautamgitspace/CellMon-UI) for the application UI. 23 | 24 | # Protobuf usage, installation and compilation 25 | Check out [my blog](http://www.acsu.buffalo.edu/~agautam2/gistblog/furtherdown/protocolbuffers.html) to get up and running with protocol buffers 26 | 27 | # License 28 | This project is licensed under the [MIT License](https://en.wikipedia.org/wiki/MIT_License). 29 | 30 | Copyright (c) 2016 [Abhishek Gautam](http://www.acsu.buffalo.edu/~agautam2/), [Armaan Goyal](http://www.acsu.buffalo.edu/~armaango/) and [UB Computer Science](https://www.cse.buffalo.edu/) 31 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.google.protobuf" version "0.8.0" 3 | } 4 | apply plugin: 'com.android.application' 5 | 6 | repositories { 7 | maven { url "https://jitpack.io" } 8 | } 9 | 10 | android { 11 | compileSdkVersion 23 12 | buildToolsVersion '25.0.0' 13 | 14 | useLibrary 'org.apache.http.legacy' 15 | defaultConfig { 16 | applicationId "edu.buffalo.cse.ubwins.cellmon" 17 | minSdkVersion 18 18 | targetSdkVersion 21 19 | versionCode 1 20 | versionName "1.0" 21 | multiDexEnabled true 22 | } 23 | buildTypes { 24 | release { 25 | minifyEnabled false 26 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 27 | } 28 | } 29 | } 30 | 31 | dependencies { 32 | compile fileTree(dir: 'libs', include: ['*.jar']) 33 | testCompile 'junit:junit:4.12' 34 | compile 'com.android.support:appcompat-v7:23.4.0' 35 | compile 'com.android.support:design:23.0.1' 36 | compile 'com.android.support:support-v4:23.4.0' 37 | compile 'com.google.android.gms:play-services:10.2.0' 38 | compile 'com.android.support:multidex:1.0.1' 39 | compile 'com.google.protobuf:protobuf-lite:3.0.0' 40 | compile 'com.pushlink:pushlink-android:5.5.0' 41 | compile 'com.facebook.stetho:stetho:1.4.2' 42 | compile 'com.google.maps.android:android-maps-utils:0.5+' 43 | } 44 | 45 | protobuf { 46 | plugins { 47 | javalite { 48 | // The codegen for lite comes as a separate artifact 49 | artifact = 'com.google.protobuf:protoc-gen-javalite:3.0.0' 50 | } 51 | 52 | } 53 | protoc 54 | { 55 | path = '/usr/bin/protoc' 56 | path = '/usr/local/bin/protoc' 57 | } 58 | 59 | generatedFilesBaseDir = "$projectDir/src/protobuf_autogen" 60 | 61 | generateProtoTasks { 62 | all().each { task -> 63 | task.plugins { 64 | javalite { 65 | outputSubDir = 'java' 66 | } 67 | } 68 | } 69 | } 70 | } 71 | apply plugin: 'com.google.gms.google-services' -------------------------------------------------------------------------------- /app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "62153221471", 4 | "firebase_url": "https://eminent-parsec-158802.firebaseio.com", 5 | "project_id": "eminent-parsec-158802", 6 | "storage_bucket": "eminent-parsec-158802.appspot.com" 7 | }, 8 | "client": [ 9 | { 10 | "client_info": { 11 | "mobilesdk_app_id": "1:62153221471:android:a6d915737041af62", 12 | "android_client_info": { 13 | "package_name": "edu.buffalo.cse.ubwins.cellmon" 14 | } 15 | }, 16 | "oauth_client": [ 17 | { 18 | "client_id": "62153221471-fgct8koqqdv56ffieijlhbql0tobhg5g.apps.googleusercontent.com", 19 | "client_type": 3 20 | } 21 | ], 22 | "api_key": [ 23 | { 24 | "current_key": "AIzaSyA_ViTr-Tyypip3_ii3H0CcJyHo6i0_t68" 25 | } 26 | ], 27 | "services": { 28 | "analytics_service": { 29 | "status": 1 30 | }, 31 | "appinvite_service": { 32 | "status": 1, 33 | "other_platform_oauth_client": [] 34 | }, 35 | "ads_service": { 36 | "status": 2 37 | } 38 | } 39 | } 40 | ], 41 | "configuration_version": "1" 42 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/Gautam/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/edu/buffalo/cse/ubwins/cellmon/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package edu.buffalo.cse.ubwins.cellmon; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 25 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /app/src/main/java/edu/buffalo/cse/ubwins/cellmon/Alarm.java: -------------------------------------------------------------------------------- 1 | package edu.buffalo.cse.ubwins.cellmon; 2 | 3 | import android.annotation.TargetApi; 4 | import android.app.AlarmManager; 5 | import android.app.PendingIntent; 6 | import android.content.BroadcastReceiver; 7 | import android.content.Context; 8 | import android.content.Intent; 9 | import android.location.LocationManager; 10 | import android.os.AsyncTask; 11 | import android.os.PowerManager; 12 | import android.telephony.TelephonyManager; 13 | import android.util.Base64; 14 | import android.util.Log; 15 | 16 | import com.google.firebase.crash.FirebaseCrash; 17 | 18 | import org.apache.http.HttpResponse; 19 | import org.apache.http.client.ClientProtocolException; 20 | import org.apache.http.client.HttpClient; 21 | import org.apache.http.client.methods.HttpGet; 22 | import org.apache.http.impl.client.DefaultHttpClient; 23 | 24 | import java.io.IOException; 25 | import java.net.URI; 26 | import java.net.URISyntaxException; 27 | import java.net.URLEncoder; 28 | import java.security.MessageDigest; 29 | import java.security.NoSuchAlgorithmException; 30 | 31 | import static android.content.Context.LOCATION_SERVICE; 32 | 33 | /** 34 | * Created by pcoonan on 4/12/17. 35 | */ 36 | 37 | public class Alarm extends BroadcastReceiver { 38 | CellularDataRecorder cdr; 39 | PhoneCallStateRecorder pcsr; 40 | DBstore dbStore; 41 | public final String TAG = "[CELNETMON-ALARM]"; 42 | static int keepAlive = 0; 43 | String IMEI_HASH; 44 | String IMEI; 45 | public static long lastTime = 0; 46 | 47 | @Override 48 | public void onReceive(Context context, Intent intent) { 49 | // long cur = System.currentTimeMillis(); 50 | // Log.d(TAG, "Received alarm trigger since " + ((cur - lastTime)/1000.0)); 51 | // PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 52 | // PowerManager.WakeLock wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, ""); 53 | // wakeLock.acquire(); 54 | 55 | logData(context); 56 | setAlarm(context); 57 | // wakeLock.release(); 58 | } 59 | 60 | @TargetApi(19) 61 | public void setAlarm(Context context){ 62 | AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 63 | Intent i = new Intent(context, Alarm.class); 64 | i.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); 65 | PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, PendingIntent.FLAG_UPDATE_CURRENT); 66 | am.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+10000, pi); 67 | lastTime = System.currentTimeMillis(); 68 | // am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + (10000 * offset), 60000, pi); 69 | // Log.d(TAG, "Alarm set!"); 70 | } 71 | 72 | public void cancelAlarm(Context context){ 73 | Intent intent = new Intent(context, Alarm.class); 74 | PendingIntent sender = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); 75 | AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 76 | alarmManager.cancel(sender); 77 | Log.d(TAG, "Alarm cancelled!"); 78 | } 79 | 80 | private void logData(Context arg0) { 81 | keepAlive++; 82 | 83 | final TelephonyManager telephonyManager = 84 | (TelephonyManager) arg0.getSystemService(Context.TELEPHONY_SERVICE); 85 | IMEI = telephonyManager.getDeviceId(); 86 | cdr = new CellularDataRecorder(); 87 | pcsr = new PhoneCallStateRecorder(); 88 | 89 | // locationFinder = new LocationFinder(arg0); 90 | //Log.v(TAG, "Calling getLocalTimeStamp and getCellularInfo"); 91 | 92 | /*FETCH INFO FROM CDR CLASS*/ 93 | Long timeStamp = cdr.getLocalTimeStamp(); 94 | String cellularInfo = cdr.getCellularInfo(telephonyManager); 95 | int dataActivity = cdr.getCurrentDataActivity(telephonyManager); 96 | int dataState = cdr.getCurrentDataState(telephonyManager); 97 | int mobileNetworkType = cdr.getMobileNetworkType(telephonyManager); 98 | 99 | final LocationManager locationManager = (LocationManager) arg0.getSystemService(LOCATION_SERVICE); 100 | 101 | if (ForegroundService.FusedApiLatitude == null || ForegroundService.FusedApiLongitude == null) { 102 | return; 103 | } 104 | 105 | /*FETCH INFO FROM FUSED API*/ 106 | Double fusedApiLatitude = ForegroundService.FusedApiLatitude; 107 | Double fusedApiLongitude = ForegroundService.FusedApiLongitude; 108 | boolean stale = ((System.currentTimeMillis() - ForegroundService.LastFusedLocation) > 40000) 109 | && dataState == 0 && !locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER); 110 | 111 | Double locationdata[] = {fusedApiLatitude, fusedApiLongitude}; 112 | 113 | 114 | /*FETCH INFO FROM PCSR CLASS*/ 115 | int phoneCallState = PhoneCallStateRecorder.call_state; 116 | 117 | dbStore = new DBstore(arg0); 118 | dbStore.insertIntoDB(locationdata, stale, timeStamp, cellularInfo, dataActivity, dataState, 119 | phoneCallState, mobileNetworkType); 120 | 121 | if (keepAlive == 360) { 122 | new PingTask().execute(); 123 | keepAlive = 0; 124 | } 125 | } 126 | 127 | class PingTask extends AsyncTask { 128 | 129 | @Override 130 | protected String doInBackground(String... params) { 131 | String IMEI_HASH = ""; 132 | String responseStr = ""; 133 | try { 134 | /*HASH IMEI*/ 135 | IMEI_HASH = genHash(IMEI); 136 | } catch (NoSuchAlgorithmException e) { 137 | e.printStackTrace(); 138 | } 139 | Log.v(TAG, "GENERATED IMEI HASH"); 140 | //TODO KEEP-ALIVE GET 141 | HttpResponse response = null; 142 | try { 143 | HttpClient client = new DefaultHttpClient(); 144 | HttpGet request = new HttpGet(); 145 | String customURL = "http://104.196.177.7/aggregator/ping?imei_hash=" 146 | + URLEncoder.encode(IMEI_HASH, "UTF-8"); 147 | Log.d(TAG, customURL); 148 | request.setURI(new URI(customURL)); 149 | response = client.execute(request); 150 | Log.v(TAG, "RESPONSE PHRASE FOR HTTP GET: " 151 | + response.getStatusLine().getReasonPhrase()); 152 | Log.v(TAG, "RESPONSE STATUS FOR HTTP GET: " 153 | + response.getStatusLine().getStatusCode()); 154 | responseStr = response.getStatusLine().getReasonPhrase(); 155 | } catch (URISyntaxException|IOException e) { 156 | e.printStackTrace(); 157 | FirebaseCrash.log("Error in ping task for: " + IMEI_HASH); 158 | } 159 | return responseStr; 160 | } 161 | } 162 | 163 | private String genHash(String input) throws NoSuchAlgorithmException { 164 | String IMEI_Base64 = ""; 165 | try { 166 | MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); 167 | byte[] sha256Hash = sha256.digest(input.getBytes("UTF-8")); 168 | IMEI_Base64 = Base64.encodeToString(sha256Hash, Base64.DEFAULT); 169 | IMEI_Base64 = IMEI_Base64.replaceAll("\n", ""); 170 | } catch (Exception e) { 171 | e.printStackTrace(); 172 | } 173 | return IMEI_Base64; 174 | } 175 | 176 | 177 | } 178 | -------------------------------------------------------------------------------- /app/src/main/java/edu/buffalo/cse/ubwins/cellmon/CellularDataRecorder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Gautam on 6/18/16. 3 | * MBP111.0138.B16 4 | * agautam2@buffalo.edu 5 | * University at Buffalo, The State University of New York. 6 | * Copyright © 2016 Gautam. All rights reserved. 7 | */ 8 | 9 | package edu.buffalo.cse.ubwins.cellmon; 10 | 11 | import android.telephony.CellIdentityGsm; 12 | import android.telephony.CellIdentityLte; 13 | import android.telephony.CellIdentityWcdma; 14 | import android.telephony.CellIdentityCdma; 15 | import android.telephony.CellInfo; 16 | import android.telephony.CellInfoCdma; 17 | import android.telephony.CellInfoGsm; 18 | import android.telephony.CellInfoLte; 19 | import android.telephony.CellInfoWcdma; 20 | import android.telephony.CellSignalStrengthCdma; 21 | import android.telephony.CellSignalStrengthGsm; 22 | import android.telephony.CellSignalStrengthLte; 23 | import android.telephony.CellSignalStrengthWcdma; 24 | import android.telephony.TelephonyManager; 25 | import android.util.Log; 26 | 27 | 28 | 29 | public class CellularDataRecorder 30 | { 31 | 32 | static final String TAG = "[CELNETMON-CDR]"; 33 | 34 | public Long getLocalTimeStamp() 35 | { 36 | 37 | Long timeStamp = System.currentTimeMillis(); 38 | return timeStamp; 39 | } 40 | 41 | public int getCurrentDataState(TelephonyManager telephonyManager) 42 | { 43 | int state = telephonyManager.getDataState(); 44 | int dataState; 45 | 46 | if (state < 0 || state >3 ){ 47 | dataState = -1; 48 | } 49 | else{ 50 | dataState =state; 51 | } 52 | return dataState; 53 | } 54 | 55 | public int getCurrentDataActivity(TelephonyManager telephonyManager) 56 | { 57 | int activity = telephonyManager.getDataActivity(); 58 | int dataActivity; 59 | 60 | if (activity < 0 || activity > 4 ){ 61 | dataActivity = -1; 62 | } 63 | else{ 64 | dataActivity =activity; 65 | } 66 | return dataActivity; 67 | } 68 | 69 | public int getMobileNetworkType(TelephonyManager telephonyManager) 70 | { 71 | int networkType = telephonyManager.getNetworkType(); 72 | 73 | return networkType; 74 | } 75 | 76 | public String getCellularInfo(TelephonyManager telephonyManager) 77 | { 78 | String cellularInfo = ""; 79 | String log = ""; 80 | if(telephonyManager.getAllCellInfo()==null) { 81 | Log.v(TAG, "getAllCellInfo returned null"); 82 | } 83 | else { 84 | for (final CellInfo info : telephonyManager.getAllCellInfo()) { 85 | if (info instanceof CellInfoGsm) { 86 | log += "GSM@"; 87 | CellIdentityGsm gsm_cell = ((CellInfoGsm) info).getCellIdentity(); 88 | log += gsm_cell.getCid() + "#" + gsm_cell.getLac() + "#" + gsm_cell.getMcc() + "#" + gsm_cell.getMnc() + "_"; 89 | 90 | final CellSignalStrengthGsm gsm = ((CellInfoGsm) info).getCellSignalStrength(); 91 | log += gsm.getDbm() + "#" + gsm.getLevel()+"#"+gsm.getAsuLevel()+":"; 92 | } else if (info instanceof CellInfoCdma) { 93 | log += "CDMA@"; 94 | CellIdentityCdma cdma_cell = ((CellInfoCdma) info).getCellIdentity(); 95 | log += cdma_cell.getBasestationId() + "#" + cdma_cell.getNetworkId() + "#" + cdma_cell.getSystemId() + "#" + cdma_cell.getSystemId() + "_"; 96 | 97 | final CellSignalStrengthCdma cdma = ((CellInfoCdma) info).getCellSignalStrength(); 98 | log += cdma.getDbm() + "#" + cdma.getLevel()+"#"+cdma.getAsuLevel()+":"; 99 | } else if (info instanceof CellInfoLte) { 100 | log += "LTE@"; 101 | CellIdentityLte lte_cell = ((CellInfoLte) info).getCellIdentity(); 102 | log += lte_cell.getCi() + "#" + lte_cell.getPci() + "#" + lte_cell.getMcc() + "#" + lte_cell.getMnc() + "_"; 103 | 104 | final CellSignalStrengthLte lte = ((CellInfoLte) info).getCellSignalStrength(); 105 | log += lte.getDbm() + "#" + lte.getLevel()+"#"+lte.getAsuLevel()+":"; 106 | } else if (info instanceof CellInfoWcdma) { 107 | log += "WCDMA@"; 108 | CellIdentityWcdma wcdma_cell = ((CellInfoWcdma) info).getCellIdentity(); 109 | log += wcdma_cell.getCid() + "#" + wcdma_cell.getLac() + "#" + wcdma_cell.getMcc() + "#" + wcdma_cell.getMnc() + "_"; 110 | 111 | final CellSignalStrengthWcdma wcdma = ((CellInfoWcdma) info).getCellSignalStrength(); 112 | log += wcdma.getDbm() + "#" + wcdma.getLevel()+"#"+wcdma.getAsuLevel()+":"; 113 | } else { 114 | Log.v(TAG, "Unknown Network Type"); 115 | } 116 | } 117 | } 118 | cellularInfo = log; 119 | return cellularInfo; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /app/src/main/java/edu/buffalo/cse/ubwins/cellmon/Cluster.java: -------------------------------------------------------------------------------- 1 | package edu.buffalo.cse.ubwins.cellmon; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | 7 | /** 8 | * Created by pcoonan on 5/12/17. 9 | */ 10 | 11 | public class Cluster { 12 | public List entries; 13 | public Coordinate centroid; 14 | public int id; 15 | 16 | public Cluster(int id){ 17 | this.id = id; 18 | this.entries = new ArrayList(); 19 | this.centroid = null; 20 | } 21 | 22 | public List getEntries(){ 23 | return entries; 24 | } 25 | 26 | public void addEntry(Entry entry){ 27 | entries.add(entry); 28 | } 29 | 30 | public void setEntries(List entries){ 31 | this.entries = entries; 32 | } 33 | 34 | public Coordinate getCentroid() { 35 | return centroid; 36 | } 37 | 38 | public void setCentroid(Coordinate centroid) { 39 | this.centroid = centroid; 40 | } 41 | 42 | public int getId() { 43 | return id; 44 | } 45 | 46 | public void clear(){ 47 | entries.clear(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/edu/buffalo/cse/ubwins/cellmon/Coordinate.java: -------------------------------------------------------------------------------- 1 | package edu.buffalo.cse.ubwins.cellmon; 2 | 3 | import com.google.android.gms.maps.model.LatLng; 4 | 5 | import java.util.Random; 6 | 7 | /** 8 | * Created by pcoonan on 5/12/17. 9 | */ 10 | 11 | public class Coordinate { 12 | private LatLng position; 13 | 14 | public Coordinate(double latitude, double longitude){ 15 | this.position = new LatLng(latitude, longitude); 16 | } 17 | 18 | public void setLatitude(double latitude){ 19 | this.position = new LatLng(latitude, position.longitude); 20 | } 21 | 22 | public void setLongitude(double longitude){ 23 | this.position = new LatLng(position.latitude, longitude); 24 | } 25 | 26 | public double getLatitude(){ 27 | return this.position.latitude; 28 | } 29 | 30 | public double getLongitude(){ 31 | return this.position.longitude; 32 | } 33 | 34 | public LatLng getPosition(){ 35 | return this.position; 36 | } 37 | 38 | protected static double distance(Coordinate c, Coordinate centroid){ 39 | return Math.sqrt( 40 | Math.pow((centroid.getLatitude() - c.getLatitude()), 2) + 41 | Math.pow((centroid.getLongitude() - c.getLongitude()), 2)); 42 | } 43 | 44 | protected static Coordinate createRandomCoordinate(double maxLat, double minLat, 45 | double minLong, double maxLong){ 46 | Random r = new Random(); 47 | double latitude = minLat + (maxLat - minLat) * r.nextDouble(); 48 | double longitude = minLong + (maxLong - minLong) * r.nextDouble(); 49 | return new Coordinate(latitude, longitude); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/java/edu/buffalo/cse/ubwins/cellmon/DBHandler.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Gautam on 6/18/16. 3 | * MBP111.0138.B16 4 | * agautam2@buffalo.edu 5 | * University at Buffalo, The State University of New York. 6 | * Copyright © 2016 Gautam. All rights reserved. 7 | */ 8 | package edu.buffalo.cse.ubwins.cellmon; 9 | 10 | import android.content.Context; 11 | import android.database.sqlite.SQLiteDatabase; 12 | import android.database.sqlite.SQLiteOpenHelper; 13 | import android.util.Log; 14 | 15 | /*Understanding of SQLite for Android - http://developer.android.com/training/basics/data-storage/databases.html */ 16 | 17 | public class DBHandler extends SQLiteOpenHelper 18 | { 19 | private static String dbName="mainTuple"; 20 | private static String dbNameBatt="mainTuple"; 21 | private static int version=2; 22 | static final String TAG = "[CELNETMON-DBHANDLER]"; 23 | 24 | private static final String schema = "CREATE TABLE cellRecords (ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, F_LAT DATA, F_LONG DATA, F_STALE DATA, TIMESTAMP DATA, NETWORK_TYPE DATA, NETWORK_TYPE2 DATA, NETWORK_PARAM1 DATA, NETWORK_PARAM2 DATA, NETWORK_PARAM3 DATA, NETWORK_PARAM4 DATA, DBM DATA, NETWORK_LEVEL DATA,ASU_LEVEL DATA, DATA_STATE DATA, DATA_ACTIVITY DATA, CALL_STATE DATA)"; 25 | private static final String mapSchema = "CREATE TABLE mapRecords (ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, F_LAT DATA, F_LONG DATA, F_STALE DATA, TIMESTAMP DATA, NETWORK_TYPE DATA, NETWORK_TYPE2 DATA, NETWORK_PARAM1 DATA, NETWORK_PARAM2 DATA, NETWORK_PARAM3 DATA, NETWORK_PARAM4 DATA, DBM DATA, NETWORK_LEVEL DATA,ASU_LEVEL DATA, DATA_STATE DATA, DATA_ACTIVITY DATA, CALL_STATE DATA)"; 26 | private static final String schemeBatteryStatus = "CREATE TABLE batteryStatus (TIMESTAMP DATA, BATTERY_LEVEL DATA)"; 27 | public DBHandler(Context context) 28 | 29 | { 30 | super(context, dbName, null, version); 31 | } 32 | 33 | @Override 34 | public void onCreate(SQLiteDatabase db) 35 | { 36 | db.execSQL(schema); 37 | Log.v(TAG, "CREATING DB " + dbName + "WITH TABLE cellRecords"); 38 | db.execSQL(mapSchema); 39 | db.execSQL(schemeBatteryStatus); 40 | Log.v(TAG, "CREATING DB " + dbNameBatt + "WITH TABLE batteryStatus"); 41 | } 42 | 43 | @Override 44 | public void onUpgrade(SQLiteDatabase db, int obsolete, int latest) 45 | { 46 | //logic understood from - http://stackoverflow.com/questions/3675032/drop-existing-table-in-sqlite-when-if-exists-operator-is-not-supported 47 | db.execSQL("DROP TABLE IF EXISTS cellRecords"); 48 | db.execSQL("DROP TABLE IF EXISTS mapRecords"); 49 | db.execSQL("DROP TABLE IF EXISTS batteryStatus"); 50 | onCreate(db); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /app/src/main/java/edu/buffalo/cse/ubwins/cellmon/DBstore.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Gautam on 6/18/16. 3 | * MBP111.0138.B16 4 | * agautam2@buffalo.edu 5 | * University at Buffalo, The State University of New York. 6 | * Copyright © 2016 Gautam. All rights reserved. 7 | */ 8 | 9 | package edu.buffalo.cse.ubwins.cellmon; 10 | 11 | import android.content.ContentValues; 12 | import android.content.Context; 13 | import android.database.DatabaseUtils; 14 | import android.database.sqlite.SQLiteDatabase; 15 | import android.location.LocationManager; 16 | 17 | import com.google.firebase.crash.FirebaseCrash; 18 | 19 | 20 | public class DBstore 21 | { 22 | static final String TAG = "[CELNETMON-DBSTORE]"; 23 | private final Context mContext; 24 | 25 | 26 | public DBstore(Context context) 27 | { 28 | this.mContext=context; 29 | } 30 | 31 | public void insertIntoDB(Double[] locationdata, boolean stale, Long timeStamp, 32 | String cellularInfo, int dataActivity, int dataState, 33 | int phoneCallState, int mobileNetworkType) 34 | { 35 | String networkType = ""; 36 | int networkTypeval = -1; 37 | String networkState = ""; 38 | String networkStateVariables[]={"","","",""}; 39 | String networkRSSI = ""; 40 | String networkRSSIVariables[]={"",""}; 41 | ContentValues contentValues = new ContentValues(); 42 | DBHandler dbHandler = new DBHandler(mContext); 43 | SQLiteDatabase sqLiteDatabase = dbHandler.getWritableDatabase(); 44 | 45 | if(!(cellularInfo.equals(""))) 46 | { 47 | 48 | // Log.v(TAG, "before split: " + cellularInfo); 49 | String[] mainsplit = cellularInfo.split(":"); 50 | String[] splitter = mainsplit[0].split("@"); 51 | //Log.v(TAG, "splitter of zero: " + splitter[0]); 52 | //Log.v(TAG, "splitter of one: " + splitter[1]); 53 | networkType = splitter[0]; 54 | String splitter1[] = splitter[1].split("_"); 55 | networkState = splitter1[0]; 56 | networkStateVariables = networkState.split("#"); 57 | 58 | //Log.v(TAG, "splitter1 of zero: " + splitter1[0]); 59 | //Log.v(TAG, "splitter1 of one: " + splitter1[1]); 60 | networkRSSI = splitter1[1]; 61 | networkRSSIVariables = networkRSSI.split("#"); 62 | } 63 | else{ 64 | FirebaseCrash.log("Cellular info equals an empty string"); 65 | sqLiteDatabase.close(); 66 | return; 67 | } 68 | 69 | if (networkType!=null && networkType.equals("GSM")){ 70 | networkTypeval = 0; 71 | } 72 | else if (networkType!=null && networkType.equals("CDMA")){ 73 | networkTypeval = 1; 74 | } 75 | else if (networkType!=null && networkType.equals("LTE")){ 76 | networkTypeval = 2; 77 | } 78 | else if (networkType!=null && networkType.equals("WCDMA")){ 79 | networkTypeval = 3; 80 | } 81 | //Log.v(TAG,"Trying to push to DB"); 82 | 83 | contentValues.put("F_LAT",locationdata[0]); 84 | contentValues.put("F_LONG",locationdata[1]); 85 | contentValues.put("F_STALE", stale); 86 | contentValues.put("TIMESTAMP",timeStamp); 87 | contentValues.put("NETWORK_TYPE", networkTypeval); 88 | contentValues.put("NETWORK_TYPE2", mobileNetworkType); 89 | contentValues.put("NETWORK_PARAM1", Integer.parseInt(networkStateVariables[0])); 90 | contentValues.put("NETWORK_PARAM2", Integer.parseInt(networkStateVariables[1])); 91 | contentValues.put("NETWORK_PARAM3", Integer.parseInt(networkStateVariables[2])); 92 | contentValues.put("NETWORK_PARAM4", Integer.parseInt(networkStateVariables[3])); 93 | contentValues.put("DBM", Integer.parseInt(networkRSSIVariables[0])); 94 | contentValues.put("NETWORK_LEVEL", Integer.parseInt(networkRSSIVariables[1])); 95 | contentValues.put("ASU_LEVEL",Integer.parseInt(networkRSSIVariables[2])); 96 | contentValues.put("DATA_STATE",dataState); 97 | contentValues.put("DATA_ACTIVITY", dataActivity); 98 | contentValues.put("CALL_STATE",phoneCallState); 99 | 100 | sqLiteDatabase.insert("cellRecords", null, contentValues); 101 | 102 | // Only store one hour of data for map testing 103 | long mapCount = DatabaseUtils.queryNumEntries(sqLiteDatabase, "mapRecords"); 104 | if(mapCount < 1200){ 105 | sqLiteDatabase.insert("mapRecords", null, contentValues); 106 | } 107 | sqLiteDatabase.close(); 108 | //Log.v(TAG,"Push to DB Successful"); 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /app/src/main/java/edu/buffalo/cse/ubwins/cellmon/DatePickerFragment.java: -------------------------------------------------------------------------------- 1 | package edu.buffalo.cse.ubwins.cellmon; 2 | 3 | import android.app.Dialog; 4 | import android.app.DialogFragment; 5 | import android.app.DatePickerDialog; 6 | import android.os.Bundle; 7 | import android.util.Log; 8 | import android.view.View; 9 | import android.widget.DatePicker; 10 | 11 | import java.text.SimpleDateFormat; 12 | import java.util.Calendar; 13 | 14 | import static android.R.attr.max; 15 | import static android.R.attr.minDate; 16 | import static android.media.CamcorderProfile.get; 17 | import static android.os.Build.VERSION_CODES.M; 18 | 19 | /** 20 | * Created by pcoonan on 3/31/17. 21 | */ 22 | 23 | public class DatePickerFragment extends DialogFragment implements DatePickerDialog.OnDateSetListener { 24 | 25 | public final String TAG = "[CELNETMON-DATEFRAG]"; 26 | // public static String selectedItem; 27 | public static Integer selectedYear; 28 | public static Integer selectedMonth; 29 | public static Integer selectedDay; 30 | 31 | public static DatePickerFragment newInstance(long mindate){ 32 | DatePickerFragment fragment = new DatePickerFragment(); 33 | 34 | Bundle args = new Bundle(); 35 | args.putLong("mindate", mindate); 36 | fragment.setArguments(args); 37 | 38 | return fragment; 39 | } 40 | @Override 41 | public Dialog onCreateDialog(Bundle savedInstanceState){ 42 | Long mindate = getArguments().getLong("mindate"); 43 | final Calendar maxDate = Calendar.getInstance(); 44 | maxDate.add(Calendar.DATE, -2); 45 | 46 | int year = maxDate.get(Calendar.YEAR); 47 | int month = maxDate.get(Calendar.MONTH); 48 | int day = maxDate.get(Calendar.DAY_OF_MONTH); 49 | 50 | selectedYear = (selectedYear == null) ? year : selectedYear; 51 | selectedMonth = (selectedMonth == null) ? month : selectedMonth; 52 | selectedDay = (selectedDay == null) ? day : selectedDay; 53 | DatePickerDialog dpd = new DatePickerDialog(getActivity(), 54 | this, selectedYear, selectedMonth, selectedDay); 55 | 56 | dpd.getDatePicker().setMaxDate(maxDate.getTimeInMillis()); 57 | dpd.getDatePicker().setMinDate(mindate); 58 | return dpd; 59 | } 60 | 61 | @Override 62 | public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) { 63 | // Log.d(TAG, "Date selected: DAY:" + dayOfMonth + " MONTH:" + monthOfYear + " YEAR:" + year); 64 | selectedDay = dayOfMonth; 65 | selectedMonth = monthOfYear; 66 | selectedYear = year; 67 | sendBackResult(); 68 | } 69 | 70 | public void sendBackResult(){ 71 | DateSelectedListener listener = (DateSelectedListener) getTargetFragment(); 72 | // Need to add one to month index for conversion back to millis 73 | listener.onFinishSelect("day", (selectedMonth+1) + "/" + selectedDay + "/" + selectedYear); 74 | dismiss(); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/java/edu/buffalo/cse/ubwins/cellmon/DateSelectedListener.java: -------------------------------------------------------------------------------- 1 | package edu.buffalo.cse.ubwins.cellmon; 2 | 3 | /** 4 | * Created by pcoonan on 4/6/17. 5 | */ 6 | 7 | public interface DateSelectedListener { 8 | void onFinishSelect(String type, String value); 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/java/edu/buffalo/cse/ubwins/cellmon/Entry.java: -------------------------------------------------------------------------------- 1 | package edu.buffalo.cse.ubwins.cellmon; 2 | 3 | import com.google.android.gms.maps.model.LatLng; 4 | import com.google.maps.android.clustering.ClusterItem; 5 | 6 | import org.json.JSONException; 7 | import org.json.JSONObject; 8 | 9 | /** 10 | * Created by patrick on 5/2/17. 11 | */ 12 | 13 | public class Entry implements ClusterItem{ 14 | public long timestamp; 15 | // public double latitude; 16 | // public double longitude; 17 | public Coordinate coordinate; 18 | public int networkCellType; 19 | public int dbm; 20 | public int signalLevel; 21 | public int clusterNumber = 0; 22 | 23 | private Entry(){}; 24 | public static Entry mapJSON(JSONObject object) throws JSONException { 25 | Entry ret = new Entry(); 26 | ret.timestamp = object.getLong("timestamp"); 27 | // ret.latitude = object.getDouble("fused_lat"); 28 | // ret.longitude = object.getDouble("fused_long"); 29 | ret.coordinate = new Coordinate(object.getDouble("fused_lat"),object.getDouble("fused_long")); 30 | ret.networkCellType = object.getInt("network_cell_type"); 31 | ret.dbm = object.getInt("signal_dbm"); 32 | ret.signalLevel = object.getInt("signal_level"); 33 | return ret; 34 | } 35 | 36 | @Override 37 | public LatLng getPosition() { 38 | return coordinate.getPosition(); 39 | } 40 | 41 | @Override 42 | public String getTitle() { 43 | return String.valueOf(timestamp); 44 | } 45 | 46 | @Override 47 | public String getSnippet() { 48 | return String.valueOf(dbm); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/edu/buffalo/cse/ubwins/cellmon/ForegroundService.java: -------------------------------------------------------------------------------- 1 | package edu.buffalo.cse.ubwins.cellmon; 2 | 3 | import android.app.Activity; 4 | import android.app.Notification; 5 | import android.app.PendingIntent; 6 | import android.app.Service; 7 | import android.content.BroadcastReceiver; 8 | import android.content.ContentValues; 9 | import android.content.Context; 10 | import android.content.Intent; 11 | import android.content.IntentFilter; 12 | import android.content.SharedPreferences; 13 | import android.database.Cursor; 14 | import android.database.sqlite.SQLiteDatabase; 15 | import android.location.Location; 16 | import android.net.ConnectivityManager; 17 | import android.net.NetworkInfo; 18 | import android.os.AsyncTask; 19 | import android.os.BatteryManager; 20 | import android.os.Bundle; 21 | import android.os.Environment; 22 | import android.os.IBinder; 23 | import android.os.PowerManager; 24 | import android.support.v4.app.NotificationCompat; 25 | import android.telephony.TelephonyManager; 26 | import android.util.Base64; 27 | import android.util.Log; 28 | import android.widget.Toast; 29 | import com.google.android.gms.common.ConnectionResult; 30 | import com.google.android.gms.common.api.GoogleApiClient; 31 | import com.google.android.gms.location.LocationListener; 32 | import com.google.android.gms.location.LocationRequest; 33 | import com.google.android.gms.location.LocationServices; 34 | 35 | import org.apache.http.HttpResponse; 36 | import org.apache.http.client.HttpClient; 37 | import org.apache.http.client.methods.HttpPost; 38 | import org.apache.http.entity.ByteArrayEntity; 39 | import org.apache.http.impl.client.DefaultHttpClient; 40 | import org.apache.http.util.EntityUtils; 41 | import org.json.JSONObject; 42 | 43 | import java.io.File; 44 | import java.io.FileWriter; 45 | import java.io.IOException; 46 | import java.io.PrintWriter; 47 | import java.security.MessageDigest; 48 | import java.security.NoSuchAlgorithmException; 49 | 50 | import static edu.buffalo.cse.ubwins.cellmon.Scheduler.scheduler; 51 | 52 | public class ForegroundService extends Service implements 53 | GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, 54 | LocationListener 55 | { 56 | private static final String LOG_TAG = "ForegroundService"; 57 | 58 | ScheduleIntentReceiver scheduleIntentReceiver; 59 | // Scheduler scheduler; 60 | private GoogleApiClient mGoogleApiClient; 61 | public LocationRequest mLocationRequest; 62 | public static Double FusedApiLatitude; 63 | public static Double FusedApiLongitude; 64 | public static long LastFusedLocation; 65 | 66 | public static int TYPE_WIFI = 1; 67 | public static int TYPE_MOBILE = 2; 68 | public static int TYPE_NOT_CONNECTED = 0; 69 | 70 | String URL_UPLOAD = "http://104.196.177.7:80/aggregator/upload/"; 71 | String responsePhrase; 72 | String statusPhraseLogger; 73 | String recordsPhraseLogger; 74 | String IMEI_TO_POST; 75 | static PrintWriter printWriter = null; 76 | 77 | private SQLiteDatabase sqLiteDatabase; 78 | 79 | // Originally 5 hours and 12000 entries 80 | static int hoursPerUpload = 5; 81 | static int entriesToUpload = 12000; 82 | File exportDir; 83 | Alarm alarm = new Alarm(); 84 | private Scheduler scheduler; 85 | 86 | PowerManager.WakeLock wakeLock; 87 | SharedPreferences preferences; 88 | static FileWriter fileWriter = null; 89 | static File file = null; 90 | ContentValues contentValues = new ContentValues(); 91 | 92 | public final String TAG = "[CELMON-FRGRNDSRVC]"; 93 | 94 | public static void initPrintWriter(File file) 95 | { 96 | try 97 | { 98 | printWriter = new PrintWriter(new FileWriter(file,true)); 99 | } 100 | catch(IOException e) 101 | { 102 | e.printStackTrace(); 103 | } 104 | } 105 | 106 | 107 | @Override 108 | public void onCreate() 109 | { 110 | super.onCreate(); 111 | buildGoogleApiClient(); 112 | scheduleIntentReceiver = new ScheduleIntentReceiver(); 113 | scheduler = new Scheduler(); 114 | Log.v(LOG_TAG, "Creating Scheduler Instance"); 115 | 116 | IntentFilter filter = new IntentFilter(); 117 | filter.addAction("android.intent.action.ACTION_POWER_CONNECTED"); 118 | filter.addAction("android.intent.action.ACTION_POWER_DISCONNECTED"); 119 | filter.addAction(Intent.ACTION_BATTERY_CHANGED); 120 | 121 | registerReceiver(receiver, filter); 122 | 123 | String state = Environment.getExternalStorageState(); 124 | if (!Environment.MEDIA_MOUNTED.equals(state)) 125 | { 126 | Log.v(TAG, "MEDIA MOUNT ERROR!"); 127 | } 128 | else { 129 | exportDir = 130 | Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); 131 | 132 | if (!exportDir.exists()) 133 | { 134 | exportDir.mkdirs(); 135 | Log.v(TAG, "Directory made"); 136 | } 137 | 138 | File file = new File(exportDir.getAbsolutePath() + "BatteryLevel.csv"); 139 | 140 | if(!file.exists()) 141 | { 142 | try 143 | { 144 | Log.v(TAG, "CREATING FILE at " + exportDir.getAbsolutePath()); 145 | file.createNewFile(); 146 | initPrintWriter(file); 147 | printWriter.write("TIMESTAMP, BATTERY_LEVEL"); 148 | } 149 | catch(IOException ex) 150 | { 151 | ex.printStackTrace(); 152 | } 153 | } 154 | 155 | } 156 | } 157 | 158 | @Override 159 | public int onStartCommand(Intent intent, int flags, int startId) 160 | { 161 | 162 | if(null == intent){ 163 | return START_STICKY; 164 | } 165 | else if(null == intent.getAction()){ 166 | return START_STICKY; 167 | } 168 | 169 | if (intent.getAction().equals("startforeground")) 170 | { 171 | Log.i(LOG_TAG, "Received Start Foreground Intent "); 172 | Intent notificationIntent = new Intent(this, MainActivity.class); 173 | notificationIntent.setAction("mainAction"); 174 | notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 175 | | Intent.FLAG_ACTIVITY_CLEAR_TASK); 176 | PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, 177 | notificationIntent, 0); 178 | 179 | Notification notification = new NotificationCompat.Builder(this) 180 | .setContentTitle("CellularNetworkMonitor is running") 181 | .setSmallIcon(R.mipmap.m) 182 | .setContentIntent(pendingIntent) 183 | .setOngoing(true) 184 | .build(); 185 | startForeground(101, 186 | notification); 187 | 188 | /*ACQUIRING WAKELOCK*/ 189 | // PowerManager mgr = 190 | // (PowerManager) getApplicationContext().getSystemService(Context.POWER_SERVICE); 191 | // wakeLock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakeLock"); 192 | // wakeLock.acquire(); 193 | // Log.v(LOG_TAG, "Acquired WakeLock"); 194 | 195 | mGoogleApiClient.connect(); 196 | //finished connecting API Client 197 | // locationFinder = new LocationFinder(getApplicationContext()); 198 | //calling getLocation() from Location provider 199 | // locationFinder.getLocation(); 200 | 201 | /*CALL TO SCHEDULER METHOD*/ 202 | alarm.setAlarm(this); 203 | 204 | // scheduler.beep(getApplicationContext()); 205 | //Log.v(LOG_TAG, "SCHEDULER SET TO BEEP Every second"); 206 | Toast.makeText(getApplicationContext(), 207 | "Tracking set to ON!", Toast.LENGTH_SHORT).show(); 208 | 209 | } 210 | else if (intent.getAction().equals("stopforeground")) 211 | { 212 | /*CANCEL SCHEDULER AND RELEASE WAKELOCK*/ 213 | // if(wakeLock.isHeld()) { 214 | // wakeLock.release(); 215 | // } 216 | //Log.v(LOG_TAG, "Releasing WakeLock"); 217 | alarm.cancelAlarm(this); 218 | 219 | // Scheduler.stopScheduler(); 220 | //Log.v(LOG_TAG, "Beeping Service Stoppped"); 221 | 222 | /*to disconnect google api client*/ 223 | if(mGoogleApiClient.isConnected()) 224 | { 225 | mGoogleApiClient.disconnect(); 226 | } 227 | stopForeground(true); 228 | stopSelf(); 229 | Toast.makeText(getApplicationContext(), 230 | "Tracking set to OFF!", Toast.LENGTH_SHORT).show(); 231 | } 232 | return START_STICKY; 233 | } 234 | 235 | @Override 236 | public void onDestroy() { 237 | super.onDestroy(); 238 | unregisterReceiver(receiver); 239 | Log.i(LOG_TAG, "In onDestroy"); 240 | } 241 | 242 | @Override 243 | public IBinder onBind(Intent intent) { 244 | // Used only in case of bound services. 245 | return null; 246 | } 247 | 248 | @Override 249 | public void onConnected(Bundle bundle) { 250 | mLocationRequest = LocationRequest.create(); 251 | mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY); 252 | mLocationRequest.setInterval(10000); 253 | try { 254 | LocationServices.FusedLocationApi.requestLocationUpdates( 255 | mGoogleApiClient, mLocationRequest, this); 256 | } 257 | catch (SecurityException e) 258 | { 259 | e.printStackTrace(); 260 | } 261 | } 262 | 263 | @Override 264 | public void onLocationChanged(Location location) 265 | { 266 | FusedApiLatitude = location.getLatitude(); 267 | FusedApiLongitude = location.getLongitude(); 268 | LastFusedLocation = System.currentTimeMillis(); 269 | } 270 | 271 | @Override 272 | public void onConnectionSuspended(int i) 273 | { 274 | Log.i(LOG_TAG,"Google Api client has been suspended"); 275 | mGoogleApiClient.connect(); 276 | } 277 | 278 | @Override 279 | public void onConnectionFailed(ConnectionResult connectionResult){ 280 | Log.i(LOG_TAG,"Google Api client connection has failed"); 281 | } 282 | protected synchronized void buildGoogleApiClient() { 283 | mGoogleApiClient = new GoogleApiClient.Builder(this) 284 | .addConnectionCallbacks(this) 285 | .addOnConnectionFailedListener(this) 286 | .addApi(LocationServices.API) 287 | .build(); 288 | } 289 | 290 | private final BroadcastReceiver receiver = new BroadcastReceiver() 291 | { 292 | boolean isCharging = false; 293 | @Override 294 | public void onReceive(Context context, Intent intent) 295 | { 296 | Long timeStamp; 297 | String action = intent.getAction(); 298 | if(action.equals("android.intent.action.ACTION_POWER_CONNECTED")) 299 | { 300 | /*ACTION: CHARGING*/ 301 | Log.e("FS","Charging"); 302 | 303 | /*LOG BATTERY STATUS - WRITE TO CSV DIRECTLY*/ 304 | try 305 | { 306 | IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); 307 | Intent batteryStatus = context.registerReceiver(null, ifilter); 308 | 309 | timeStamp = System.currentTimeMillis(); 310 | int batteryLevel = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); 311 | int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1); 312 | float batteryPct = (batteryLevel / (float)scale)*100; 313 | 314 | String batteryPctStr = String.valueOf(batteryPct); 315 | 316 | Toast.makeText(getApplicationContext(), batteryPctStr , Toast.LENGTH_SHORT).show(); 317 | 318 | String record = timeStamp + "," + batteryPct; 319 | Log.v(TAG, "attempting to write battery status to log file"); 320 | File file = new File(exportDir.getAbsolutePath() + "BatteryLevel.csv"); 321 | if(file.exists()) 322 | { 323 | initPrintWriter(file); 324 | printWriter.println(record); 325 | } 326 | } 327 | catch(Exception exc) 328 | { 329 | exc.printStackTrace(); 330 | } 331 | finally 332 | { 333 | if(printWriter != null) printWriter.close(); 334 | } 335 | 336 | /*CHECK FOR WIFI*/ 337 | isCharging = true; 338 | int count = 0; 339 | 340 | /*UPLOAD 40 HOURS OF DATA IN ONE GO*/ 341 | while (isCharging && count <= 15) 342 | { 343 | //Log.v("FS","Charging: inside while loop"); 344 | int status = getConnectivityStatus(getApplicationContext()); 345 | if(status == TYPE_WIFI) 346 | { 347 | String res = onFetchClicked(); 348 | if(res.equals("DB_EMPTY")||res.equals("Data not stale enough")) 349 | { 350 | break; 351 | } 352 | count += 1; 353 | } 354 | else if(status == TYPE_MOBILE || status == TYPE_NOT_CONNECTED) 355 | { 356 | /*WI-FI DISCONNECTED. STOP UPLOADING HERE*/ 357 | break; 358 | } 359 | } 360 | } 361 | else if(action.equals("android.intent.action.ACTION_POWER_DISCONNECTED")) 362 | { 363 | /*ACTION: NOT CHARGING*/ 364 | Log.e("FS","Not Charging"); 365 | isCharging = false; 366 | 367 | /*WRITE TO CSV DIRECTLY*/ 368 | try 369 | { 370 | IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); 371 | Intent batteryStatus = context.registerReceiver(null, ifilter); 372 | 373 | timeStamp = System.currentTimeMillis(); 374 | 375 | int batteryLevel = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); 376 | int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1); 377 | float batteryPct = (batteryLevel / (float)scale)*100; 378 | String batteryPctStr = String.valueOf(batteryPct); 379 | 380 | Toast.makeText(getApplicationContext(), batteryPctStr , Toast.LENGTH_SHORT).show(); 381 | 382 | String record = timeStamp + "," + batteryPct; 383 | Log.v(TAG, "attempting to write battery status to log file"); 384 | File file = new File(exportDir.getAbsolutePath() + "BatteryLevel.csv"); 385 | if(file.exists()) 386 | { 387 | initPrintWriter(file); 388 | printWriter.println(record); 389 | } 390 | } 391 | catch(Exception exc) 392 | { 393 | exc.printStackTrace(); 394 | } 395 | finally 396 | { 397 | if(printWriter != null) printWriter.close(); 398 | } 399 | } 400 | } 401 | }; 402 | 403 | public static int getConnectivityStatus(Context context) 404 | { 405 | ConnectivityManager cm = 406 | (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 407 | 408 | NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); 409 | if (null != activeNetwork) 410 | { 411 | if(activeNetwork.getType() == ConnectivityManager.TYPE_WIFI) 412 | return TYPE_WIFI; 413 | 414 | if(activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) 415 | return TYPE_MOBILE; 416 | } 417 | return TYPE_NOT_CONNECTED; 418 | } 419 | 420 | public String onFetchClicked() 421 | { 422 | boolean isConnected = isConnected(); 423 | String res = ""; 424 | if(isConnected) 425 | { 426 | //Log.v(TAG, "isConnected = TRUE"); 427 | try { 428 | res = new LogAsyncTask().execute(URL_UPLOAD).get(); 429 | } 430 | catch(Exception e) 431 | { 432 | e.printStackTrace(); 433 | } 434 | } 435 | else 436 | { 437 | Log.v(LOG_TAG, "isConnected = FALSE"); 438 | Toast.makeText(getBaseContext(), 439 | "Device has no Internet Connectivity! " + 440 | "Please check your Network Connection and try again", 441 | Toast.LENGTH_LONG).show(); 442 | } 443 | return res; 444 | } 445 | 446 | private class LogAsyncTask extends AsyncTask 447 | { 448 | @Override 449 | protected String doInBackground(String... urls) 450 | { 451 | Log.v(LOG_TAG, "inside LogAsyncTask"); 452 | return LOG_POST(urls[0]); 453 | } 454 | @Override 455 | protected void onPostExecute(String result) 456 | { 457 | //Toast.makeText(getBaseContext(), "Attempt to POST made!", Toast.LENGTH_LONG).show(); 458 | } 459 | } 460 | 461 | 462 | 463 | public String LOG_POST(String url) 464 | { 465 | String TEMP_TAG = "[CURSOR_DATA] : "; 466 | int statusCode; 467 | String result = ""; 468 | 469 | Cursor cursor = fetchTopFromDB(entriesToUpload); 470 | int count = cursor.getCount(); 471 | DataRecordOuterClass.DataRecord.Builder dataRecord = 472 | DataRecordOuterClass.DataRecord.newBuilder(); 473 | DataRecordOuterClass.DataRecord recordToSend; 474 | boolean uploadflag = true; 475 | 476 | if(cursor.moveToFirst()) { 477 | long timeStamp = cursor.getLong(4); 478 | long currTimestamp = System.currentTimeMillis(); 479 | long timeGap = currTimestamp - timeStamp; 480 | if(timeGap < hoursPerUpload*60*60*1000) 481 | { 482 | uploadflag = false; 483 | result = "Data not stale enough"; 484 | } 485 | } 486 | 487 | if (cursor.moveToFirst() && uploadflag) 488 | { 489 | String IMEI = getIMEI(); 490 | String networkOperatorCode = getNetworkOperatorCode(); 491 | String networkOperatorName = getNetworkOperatorName(); 492 | 493 | try{ 494 | IMEI_TO_POST = genHash(IMEI); 495 | } 496 | catch(NoSuchAlgorithmException nsa) 497 | { 498 | nsa.printStackTrace(); 499 | } 500 | dataRecord.setIMEIHASH(IMEI_TO_POST); 501 | dataRecord.setNETWORKOPERATORNAME(networkOperatorName); 502 | dataRecord.setNETWORKOPERATORCODE(networkOperatorCode); 503 | 504 | do { 505 | dataRecord.addENTRY(DataRecordOuterClass.DataEntry.newBuilder() 506 | .setFUSEDLAT(cursor.getDouble(1)) 507 | .setFUSEDLONG(cursor.getDouble(2)) 508 | .setSTALE(cursor.getInt(3) > 0) 509 | .setTIMESTAMP(cursor.getLong(4)) 510 | .setNETWORKCELLTYPEValue(cursor.getInt(5)) 511 | .setNETWORKTYPEValue(cursor.getInt(6)) 512 | .setNETWORKPARAM1(cursor.getInt(7)) 513 | .setNETWORKPARAM2(cursor.getInt(8)) 514 | .setNETWORKPARAM3(cursor.getInt(9)) 515 | .setNETWORKPARAM4(cursor.getInt(10)) 516 | .setSIGNALDBM(cursor.getInt(11)) 517 | .setSIGNALLEVEL(cursor.getInt(12)) 518 | .setSIGNALASULEVEL(cursor.getInt(13)) 519 | .setNETWORKSTATEValue(cursor.getInt(14)) 520 | .setNETWORKDATAACTIVITYValue(cursor.getInt(15)) 521 | .setVOICECALLSTATEValue(cursor.getInt(16)).build()); 522 | 523 | recordToSend = dataRecord.build(); 524 | } while (cursor.moveToNext()); 525 | 526 | byte[] logToSend = recordToSend.toByteArray(); 527 | int len = logToSend.length; 528 | Log.e("SIZE","Length of 5 entries is : "+len); 529 | 530 | 531 | 532 | try { 533 | 534 | /*1. create HttpClient*/ 535 | HttpClient httpclient = new DefaultHttpClient(); 536 | 537 | /*2. make POST request to the given URL*/ 538 | HttpPost httpPost = new HttpPost(url); 539 | 540 | /*3. Build ByteArrayEntity*/ 541 | ByteArrayEntity byteArrayEntity = new ByteArrayEntity(logToSend); 542 | 543 | /*4. Set httpPost Entity*/ 544 | httpPost.setEntity(byteArrayEntity); 545 | 546 | /*5. Execute POST request to the given URL*/ 547 | HttpResponse httpResponse = httpclient.execute(httpPost); 548 | 549 | /*9. receive response as inputStream*/ 550 | statusCode = httpResponse.getStatusLine().getStatusCode(); 551 | 552 | /*CONVERT INPUT STREAM TO STRING*/ 553 | responsePhrase = EntityUtils.toString(httpResponse.getEntity()); 554 | Log.v(LOG_TAG, "RESPONSE" + responsePhrase); 555 | 556 | /*PARSE JSON RESPONSE*/ 557 | JSONObject jsonObject = new JSONObject(responsePhrase); 558 | recordsPhraseLogger = jsonObject.getString("records"); 559 | statusPhraseLogger = jsonObject.getString("status"); 560 | 561 | // Log.e(LOG_TAG, "STATUS: " + statusPhraseLogger); 562 | // Log.e(LOG_TAG, "RECORDS INSERTED: " + recordsPhraseLogger); 563 | 564 | /*DELETE FROM DB IF NO OF RECORDS FETCHED == NO OF RECORDS INSERTED*/ 565 | if(Integer.parseInt(recordsPhraseLogger)==count) 566 | { 567 | Log.e(LOG_TAG, "Attempting to delete from DB"); 568 | String rawQuery = 569 | "DELETE FROM cellRecords WHERE ID IN " + 570 | "(SELECT ID FROM cellRecords ORDER BY TIMESTAMP LIMIT " + 571 | count + ");"; 572 | DBHandler dbHandler = new DBHandler(getApplicationContext()); 573 | SQLiteDatabase sqLiteDatabase = dbHandler.getWritableDatabase(); 574 | sqLiteDatabase.beginTransaction(); 575 | sqLiteDatabase.execSQL(rawQuery); 576 | sqLiteDatabase.setTransactionSuccessful(); 577 | sqLiteDatabase.endTransaction(); 578 | sqLiteDatabase.close(); 579 | } 580 | 581 | 582 | if (statusCode != 404) 583 | { 584 | result = Integer.toString(statusCode); 585 | Log.v(LOG_TAG, "STATUS CODE: " + result); 586 | } 587 | else 588 | { 589 | result = Integer.toString(statusCode); 590 | Log.v(LOG_TAG, "STATUS CODE: " + result); 591 | } 592 | } 593 | catch (Exception e) 594 | { 595 | e.printStackTrace(); 596 | } 597 | } 598 | else 599 | { 600 | Log.e(TEMP_TAG, "DB IS BROKE AS HELL!"); 601 | result = "DB_EMPTY"; 602 | } 603 | sqLiteDatabase.close(); 604 | return result; 605 | } 606 | private String getIMEI() { 607 | TelephonyManager telephonyManager = 608 | (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); 609 | return telephonyManager.getDeviceId(); 610 | } 611 | 612 | private String getNetworkOperatorCode() { 613 | TelephonyManager telephonyManager = 614 | (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); 615 | return telephonyManager.getNetworkOperator(); 616 | } 617 | 618 | private String getNetworkOperatorName() { 619 | TelephonyManager telephonyManager = 620 | (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); 621 | return telephonyManager.getNetworkOperatorName(); 622 | } 623 | private String genHash(String input) throws NoSuchAlgorithmException 624 | { 625 | String IMEI_Base64=""; 626 | try 627 | { 628 | MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); 629 | byte[] sha256Hash = sha256.digest(input.getBytes("UTF-8")); 630 | IMEI_Base64 = Base64.encodeToString(sha256Hash, Base64.DEFAULT); 631 | IMEI_Base64=IMEI_Base64.replaceAll("\n", ""); 632 | } 633 | catch(Exception e) 634 | { 635 | e.printStackTrace(); 636 | } 637 | return IMEI_Base64; 638 | } 639 | 640 | private Cursor fetchTopFromDB(int limit) 641 | { 642 | String rawQuery = "SELECT * FROM cellRecords ORDER BY TIMESTAMP LIMIT " + limit; 643 | DBHandler dbHandler = new DBHandler(getApplicationContext()); 644 | sqLiteDatabase = dbHandler.getWritableDatabase(); 645 | return sqLiteDatabase.rawQuery(rawQuery, null); 646 | } 647 | 648 | public boolean isConnected() 649 | { 650 | ConnectivityManager connMgr = 651 | (ConnectivityManager) getSystemService(Activity.CONNECTIVITY_SERVICE); 652 | NetworkInfo networkInfo = connMgr.getActiveNetworkInfo(); 653 | return networkInfo != null && networkInfo.isConnected(); 654 | } 655 | } -------------------------------------------------------------------------------- /app/src/main/java/edu/buffalo/cse/ubwins/cellmon/HomeFragment.java: -------------------------------------------------------------------------------- 1 | package edu.buffalo.cse.ubwins.cellmon; 2 | 3 | import android.app.ActivityManager; 4 | import android.app.Fragment; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.content.SharedPreferences; 8 | import android.database.Cursor; 9 | import android.database.sqlite.SQLiteDatabase; 10 | import android.os.AsyncTask; 11 | import android.os.Bundle; 12 | import android.preference.PreferenceManager; 13 | import android.telephony.TelephonyManager; 14 | import android.util.Base64; 15 | import android.util.Log; 16 | import android.view.LayoutInflater; 17 | import android.view.Menu; 18 | import android.view.MenuInflater; 19 | import android.view.View; 20 | import android.view.ViewGroup; 21 | import android.widget.Button; 22 | import android.widget.TextView; 23 | 24 | import org.apache.http.HttpResponse; 25 | import org.apache.http.client.ClientProtocolException; 26 | import org.apache.http.client.HttpClient; 27 | import org.apache.http.client.methods.HttpGet; 28 | import org.apache.http.client.methods.HttpPost; 29 | import org.apache.http.entity.ByteArrayEntity; 30 | import org.apache.http.impl.client.DefaultHttpClient; 31 | import org.apache.http.util.EntityUtils; 32 | import org.json.JSONException; 33 | import org.json.JSONObject; 34 | 35 | import java.io.IOException; 36 | import java.net.URI; 37 | import java.net.URISyntaxException; 38 | import java.net.URLEncoder; 39 | import java.security.MessageDigest; 40 | import java.security.NoSuchAlgorithmException; 41 | import java.text.SimpleDateFormat; 42 | import java.util.Calendar; 43 | import java.util.Date; 44 | import java.util.TimeZone; 45 | 46 | import static edu.buffalo.cse.ubwins.cellmon.R.id.textView; 47 | 48 | /** 49 | * Created by pcoonan on 3/15/17. 50 | */ 51 | 52 | public class HomeFragment extends Fragment implements View.OnClickListener { 53 | 54 | public final String TAG = "[CELNETMON-HOMEFRAG]"; 55 | 56 | Button startTrackingButton; 57 | Button stopTrackingButton; 58 | SharedPreferences preferences; 59 | String URL_UPLOAD = "http://104.196.177.7:80/aggregator/upload/"; 60 | String responsePhrase; 61 | String statusPhraseLogger; 62 | String recordsPhraseLogger; 63 | String IMEI_TO_POST; 64 | 65 | @Override 66 | public void onCreate(Bundle savedInstanceState){ 67 | super.onCreate(savedInstanceState); 68 | setHasOptionsMenu(true); 69 | } 70 | @Override 71 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 72 | Bundle savedInstanceState){ 73 | Log.d(TAG, "Creating Home Fragment view."); 74 | View view = inflater.inflate(R.layout.activity_main, container, false); 75 | preferences = PreferenceManager.getDefaultSharedPreferences(getActivity()); 76 | 77 | startTrackingButton = (Button) view.findViewById(R.id.button4); 78 | stopTrackingButton = (Button) view.findViewById(R.id.button5); 79 | 80 | if(isMyServiceRunning(ForegroundService.class)){ 81 | startTrackingButton.setEnabled(false); 82 | stopTrackingButton.setEnabled(true); 83 | } 84 | else{ 85 | startTrackingButton.setEnabled(true); 86 | stopTrackingButton.setEnabled(false); 87 | } 88 | 89 | boolean isRegistered = preferences.getBoolean("isRegistered", false); 90 | TextView textView = (TextView) view.findViewById(R.id.textView30); 91 | 92 | if(isRegistered) 93 | { 94 | textView.setText("Device Already Registered!"); 95 | } 96 | else 97 | { 98 | textView.setText("Device registration failed!"); 99 | } 100 | 101 | startTrackingButton.setOnClickListener(this); 102 | stopTrackingButton.setOnClickListener(this); 103 | 104 | // Button forceUpload = (Button) view.findViewById(R.id.force_upload); 105 | // forceUpload.setOnClickListener(new View.OnClickListener() { 106 | // @Override 107 | // public void onClick(View v) { 108 | // Log.d(TAG, "Force Upload pressed"); 109 | // new ForceExportTask().execute(URL_UPLOAD); 110 | // } 111 | // }); 112 | // 113 | // Button forcePing = (Button) view.findViewById(R.id.ping); 114 | // forcePing.setOnClickListener(new View.OnClickListener() { 115 | // @Override 116 | // public void onClick(View v) { 117 | // new ForcePingTask().execute(); 118 | // } 119 | // }); 120 | // 121 | // Button mindate = (Button) view.findViewById(R.id.mindate); 122 | // mindate.setOnClickListener(new View.OnClickListener() { 123 | // @Override 124 | // public void onClick(View v) { 125 | // new MinDateTask().execute(); 126 | // } 127 | // }); 128 | return view; 129 | } 130 | 131 | @Override 132 | public void onClick(View v) { 133 | SharedPreferences.Editor editor = preferences.edit(); 134 | Log.e(TAG, "inside on click"); 135 | switch (v.getId()) { 136 | case R.id.button4: 137 | Intent startIntent = new Intent(getActivity(), ForegroundService.class); 138 | startIntent.setAction("startforeground"); 139 | getActivity().startService(startIntent); 140 | editor.putBoolean("TRACKING", true); 141 | editor.commit(); 142 | stopTrackingButton.setEnabled(true); 143 | startTrackingButton.setEnabled(false); 144 | break; 145 | case R.id.button5: 146 | if(isMyServiceRunning(ForegroundService.class)) 147 | { 148 | Log.v("STOP Button","Stopped"); 149 | Intent stopIntent = new Intent(getActivity(), ForegroundService.class); 150 | stopIntent.setAction("stopforeground"); 151 | getActivity().startService(stopIntent);} 152 | startTrackingButton.setEnabled(true); 153 | stopTrackingButton.setEnabled(false); 154 | editor.putBoolean("TRACKING", false); 155 | editor.commit(); 156 | break; 157 | default: 158 | break; 159 | } 160 | } 161 | 162 | private boolean isMyServiceRunning(Class serviceClass) { 163 | ActivityManager manager = (ActivityManager) getActivity().getSystemService(Context.ACTIVITY_SERVICE); 164 | for (ActivityManager.RunningServiceInfo service : 165 | manager.getRunningServices(Integer.MAX_VALUE)) { 166 | if (serviceClass.getName().equals(service.service.getClassName())) { 167 | return true; 168 | } 169 | } 170 | return false; 171 | } 172 | 173 | @Override 174 | public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){ 175 | inflater.inflate(R.menu.home_menu, menu); 176 | super.onCreateOptionsMenu(menu, inflater); 177 | } 178 | 179 | 180 | class ForcePingTask extends AsyncTask{ 181 | 182 | @Override 183 | protected String doInBackground(String... params) { 184 | String IMEI_HASH = ""; 185 | String responseStr = ""; 186 | try { 187 | /*HASH IMEI*/ 188 | IMEI_HASH = genHash(getIMEI()); 189 | } catch (NoSuchAlgorithmException e) { 190 | e.printStackTrace(); 191 | } 192 | Log.v(TAG, "GENERATED IMEI HASH"); 193 | //TODO KEEP-ALIVE GET 194 | HttpResponse response = null; 195 | try { 196 | HttpClient client = new DefaultHttpClient(); 197 | HttpGet request = new HttpGet(); 198 | String customURL = "http://104.196.177.7/aggregator/ping?imei_hash=" 199 | + URLEncoder.encode(IMEI_HASH, "UTF-8"); 200 | Log.d(TAG, customURL); 201 | request.setURI(new URI(customURL)); 202 | response = client.execute(request); 203 | Log.v(TAG, "RESPONSE PHRASE FOR HTTP GET: " 204 | + response.getStatusLine().getReasonPhrase()); 205 | Log.v(TAG, "RESPONSE STATUS FOR HTTP GET: " 206 | + response.getStatusLine().getStatusCode()); 207 | responseStr = response.getStatusLine().getReasonPhrase(); 208 | } catch (URISyntaxException e) { 209 | e.printStackTrace(); 210 | } catch (ClientProtocolException e) { 211 | e.printStackTrace(); 212 | } catch (IOException e) { 213 | e.printStackTrace(); 214 | } 215 | return responseStr; 216 | } 217 | } 218 | 219 | class MinDateTask extends AsyncTask{ 220 | 221 | @Override 222 | protected String doInBackground(String... params) { 223 | String IMEI_HASH = ""; 224 | String responseStr = ""; 225 | try { 226 | /*HASH IMEI*/ 227 | IMEI_HASH = genHash(getIMEI()); 228 | } catch (NoSuchAlgorithmException e) { 229 | e.printStackTrace(); 230 | } 231 | Log.v(TAG, "GENERATED IMEI HASH"); 232 | //TODO KEEP-ALIVE GET 233 | HttpResponse response = null; 234 | try { 235 | HttpClient client = new DefaultHttpClient(); 236 | HttpGet request = new HttpGet(); 237 | String customURL = "http://104.196.177.7/aggregator/mindate?imei_hash=" 238 | + URLEncoder.encode(IMEI_HASH, "UTF-8"); 239 | Log.d(TAG, customURL); 240 | request.setURI(new URI(customURL)); 241 | response = client.execute(request); 242 | Log.v(TAG, "RESPONSE PHRASE FOR HTTP GET: " 243 | + response.getStatusLine().getReasonPhrase()); 244 | Log.v(TAG, "RESPONSE STATUS FOR HTTP GET: " 245 | + response.getStatusLine().getStatusCode()); 246 | 247 | responseStr = EntityUtils.toString(response.getEntity()); 248 | Log.v(TAG, "RESPONSE" + responseStr); 249 | 250 | /*PARSE JSON RESPONSE*/ 251 | JSONObject jsonObject = new JSONObject(responseStr); 252 | String time = jsonObject.getString("timestamp"); 253 | // statusPhraseLogger = jsonObject.getString("status"); 254 | Calendar cal = Calendar.getInstance(); 255 | TimeZone tz = cal.getTimeZone(); 256 | 257 | SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss"); 258 | sdf.setTimeZone(tz); 259 | 260 | long timestamp = Long.parseLong(time); 261 | String parsed = sdf.format(new Date(timestamp)); 262 | Log.d(TAG, "Mindate: " + parsed); 263 | 264 | } catch (URISyntaxException e) { 265 | e.printStackTrace(); 266 | } catch (ClientProtocolException e) { 267 | e.printStackTrace(); 268 | } catch (IOException e) { 269 | e.printStackTrace(); 270 | } catch(JSONException e){ 271 | e.printStackTrace(); 272 | } 273 | return responseStr; 274 | } 275 | } 276 | 277 | class ForceExportTask extends AsyncTask{ 278 | @Override 279 | protected String doInBackground(String... urls){ 280 | Log.v(TAG, "inside ForceExportTask"); 281 | return FORCE_POST(urls[0]); 282 | } 283 | } 284 | 285 | public String FORCE_POST(String url) 286 | { 287 | String TEMP_TAG = "[CURSOR_DATA] : "; 288 | int statusCode; 289 | String result = ""; 290 | 291 | Cursor cursor = fetchTop12000FromDB(); 292 | int count = cursor.getCount(); 293 | DataRecordOuterClass.DataRecord.Builder dataRecord = 294 | DataRecordOuterClass.DataRecord.newBuilder(); 295 | DataRecordOuterClass.DataRecord recordToSend; 296 | 297 | 298 | if (cursor.moveToFirst()) 299 | { 300 | String IMEI = getIMEI(); 301 | String networkOperatorCode = getNetworkOperatorCode(); 302 | String networkOperatorName = getNetworkOperatorName(); 303 | 304 | try{ 305 | IMEI_TO_POST = genHash(IMEI); 306 | } 307 | catch(NoSuchAlgorithmException nsa) 308 | { 309 | nsa.printStackTrace(); 310 | } 311 | dataRecord.setIMEIHASH(IMEI_TO_POST); 312 | dataRecord.setNETWORKOPERATORNAME(networkOperatorName); 313 | dataRecord.setNETWORKOPERATORCODE(networkOperatorCode); 314 | 315 | do { 316 | dataRecord.addENTRY(DataRecordOuterClass.DataEntry.newBuilder() 317 | .setFUSEDLAT(cursor.getDouble(1)) 318 | .setFUSEDLONG(cursor.getDouble(2)) 319 | .setSTALE(cursor.getInt(3) > 0) 320 | .setTIMESTAMP(cursor.getLong(4)) 321 | .setNETWORKCELLTYPEValue(cursor.getInt(5)) 322 | .setNETWORKTYPEValue(cursor.getInt(6)) 323 | .setNETWORKPARAM1(cursor.getInt(7)) 324 | .setNETWORKPARAM2(cursor.getInt(8)) 325 | .setNETWORKPARAM3(cursor.getInt(9)) 326 | .setNETWORKPARAM4(cursor.getInt(10)) 327 | .setSIGNALDBM(cursor.getInt(11)) 328 | .setSIGNALLEVEL(cursor.getInt(12)) 329 | .setSIGNALASULEVEL(cursor.getInt(13)) 330 | .setNETWORKSTATEValue(cursor.getInt(14)) 331 | .setNETWORKDATAACTIVITYValue(cursor.getInt(15)) 332 | .setVOICECALLSTATEValue(cursor.getInt(16)).build()); 333 | 334 | recordToSend = dataRecord.build(); 335 | } while (cursor.moveToNext()); 336 | 337 | byte[] logToSend = recordToSend.toByteArray(); 338 | int len = logToSend.length; 339 | Log.e("SIZE","Length of 5 entries is : "+len); 340 | 341 | 342 | 343 | try { 344 | 345 | /*1. create HttpClient*/ 346 | HttpClient httpclient = new DefaultHttpClient(); 347 | 348 | /*2. make POST request to the given URL*/ 349 | HttpPost httpPost = new HttpPost(url); 350 | 351 | /*3. Build ByteArrayEntity*/ 352 | ByteArrayEntity byteArrayEntity = new ByteArrayEntity(logToSend); 353 | 354 | /*4. Set httpPost Entity*/ 355 | httpPost.setEntity(byteArrayEntity); 356 | 357 | /*5. Execute POST request to the given URL*/ 358 | HttpResponse httpResponse = httpclient.execute(httpPost); 359 | 360 | /*9. receive response as inputStream*/ 361 | statusCode = httpResponse.getStatusLine().getStatusCode(); 362 | 363 | /*CONVERT INPUT STREAM TO STRING*/ 364 | responsePhrase = EntityUtils.toString(httpResponse.getEntity()); 365 | Log.v(TAG, "RESPONSE" + responsePhrase); 366 | 367 | /*PARSE JSON RESPONSE*/ 368 | JSONObject jsonObject = new JSONObject(responsePhrase); 369 | recordsPhraseLogger = jsonObject.getString("records"); 370 | statusPhraseLogger = jsonObject.getString("status"); 371 | 372 | // Log.e(LOG_TAG, "STATUS: " + statusPhraseLogger); 373 | // Log.e(LOG_TAG, "RECORDS INSERTED: " + recordsPhraseLogger); 374 | 375 | /*DELETE FROM DB IF NO OF RECORDS FETCHED == NO OF RECORDS INSERTED*/ 376 | if(Integer.parseInt(recordsPhraseLogger)==count) 377 | { 378 | Log.e(TAG, "Attempting to delete from DB"); 379 | String rawQuery = 380 | "DELETE FROM cellRecords WHERE ID IN " + 381 | "(SELECT ID FROM cellRecords ORDER BY TIMESTAMP LIMIT " + 382 | count + ");"; 383 | DBHandler dbHandler = new DBHandler(getActivity().getApplicationContext()); 384 | SQLiteDatabase sqLiteDatabase = dbHandler.getWritableDatabase(); 385 | sqLiteDatabase.beginTransaction(); 386 | sqLiteDatabase.execSQL(rawQuery); 387 | sqLiteDatabase.setTransactionSuccessful(); 388 | sqLiteDatabase.endTransaction(); 389 | sqLiteDatabase.close(); 390 | } 391 | 392 | 393 | if (statusCode != 404) 394 | { 395 | result = Integer.toString(statusCode); 396 | Log.v(TAG, "STATUS CODE: " + result); 397 | } 398 | else 399 | { 400 | result = Integer.toString(statusCode); 401 | Log.v(TAG, "STATUS CODE: " + result); 402 | } 403 | } 404 | catch (Exception e) 405 | { 406 | e.printStackTrace(); 407 | } 408 | } 409 | else 410 | { 411 | Log.e(TEMP_TAG, "DB IS BROKE AS HELL!"); 412 | result = "DB_EMPTY"; 413 | } 414 | return result; 415 | } 416 | 417 | private String genHash(String input) throws NoSuchAlgorithmException 418 | { 419 | String IMEI_Base64=""; 420 | try 421 | { 422 | MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); 423 | byte[] sha256Hash = sha256.digest(input.getBytes("UTF-8")); 424 | IMEI_Base64 = Base64.encodeToString(sha256Hash, Base64.DEFAULT); 425 | IMEI_Base64=IMEI_Base64.replaceAll("\n", ""); 426 | } 427 | catch(Exception e) 428 | { 429 | e.printStackTrace(); 430 | } 431 | return IMEI_Base64; 432 | } 433 | 434 | private String getIMEI() { 435 | TelephonyManager telephonyManager = 436 | (TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE); 437 | return telephonyManager.getDeviceId(); 438 | } 439 | 440 | private String getNetworkOperatorCode() { 441 | TelephonyManager telephonyManager = 442 | (TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE); 443 | return telephonyManager.getNetworkOperator(); 444 | } 445 | 446 | private String getNetworkOperatorName() { 447 | TelephonyManager telephonyManager = 448 | (TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE); 449 | return telephonyManager.getNetworkOperatorName(); 450 | } 451 | 452 | private Cursor fetchTop12000FromDB() 453 | { 454 | String rawQuery = "SELECT * FROM cellRecords ORDER BY TIMESTAMP LIMIT 12000"; 455 | DBHandler dbHandler = new DBHandler(getActivity().getApplicationContext()); 456 | SQLiteDatabase sqLiteDatabase = dbHandler.getWritableDatabase(); 457 | Cursor cursor = sqLiteDatabase.rawQuery(rawQuery, null); 458 | return cursor; 459 | } 460 | } 461 | -------------------------------------------------------------------------------- /app/src/main/java/edu/buffalo/cse/ubwins/cellmon/LocationFinder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Gautam on 6/18/16. 3 | * MBP111.0138.B16 4 | * agautam2@buffalo.edu 5 | * University at Buffalo, The State University of New York. 6 | * Copyright © 2016 Gautam. All rights reserved. 7 | */ 8 | 9 | package edu.buffalo.cse.ubwins.cellmon; 10 | 11 | import android.app.Service; 12 | import android.content.Context; 13 | import android.content.Intent; 14 | import android.location.Location; 15 | import android.location.LocationListener; 16 | import android.location.LocationManager; 17 | import android.location.LocationProvider; 18 | import android.os.Bundle; 19 | import android.os.IBinder; 20 | import android.util.Log; 21 | 22 | 23 | public class LocationFinder extends Service implements LocationListener 24 | { 25 | private final Context mContext; 26 | 27 | public static double latitude; 28 | public static double longitude; 29 | 30 | // We want instant results once we start listening so we can minimize the amount of time 31 | // the gps is on 32 | private static final long distance = 0; 33 | private static final long updateInterval = 0; 34 | boolean isGPSEnabled = false; 35 | public static boolean isGPSUpdated = false; 36 | static final String TAG = "[CELNETMON-LOCFINDER]"; 37 | protected LocationManager locationManager; 38 | 39 | 40 | 41 | public LocationFinder(Context context) 42 | { 43 | this.mContext = context; 44 | } 45 | 46 | public void getLocation() 47 | { 48 | 49 | locationManager = (LocationManager) mContext.getSystemService(LOCATION_SERVICE); 50 | isGPSEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER); 51 | if (isGPSEnabled) 52 | { 53 | try 54 | { 55 | locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, updateInterval, distance, this); 56 | } 57 | catch (SecurityException s) 58 | { 59 | s.printStackTrace(); 60 | } 61 | } 62 | } 63 | 64 | public void stopUpdates(){ 65 | locationManager = (LocationManager) mContext.getSystemService(LOCATION_SERVICE); 66 | locationManager.removeUpdates(this); 67 | } 68 | 69 | @Override 70 | public void onLocationChanged(Location location) 71 | { 72 | latitude = location.getLatitude(); 73 | longitude = location.getLongitude(); 74 | isGPSUpdated = true; 75 | stopUpdates(); 76 | } 77 | 78 | @Override 79 | public void onProviderDisabled(String provider) 80 | { 81 | //Log.v(TAG,"inside Provider disabled"); 82 | if (provider == LocationManager.GPS_PROVIDER){ 83 | Log.v(TAG,"GPS Provider disabled by user"); 84 | //can make toast here 85 | } 86 | else if(provider == LocationManager.NETWORK_PROVIDER){ 87 | Log.v(TAG,"NETWORK Provider disabled by user"); 88 | 89 | //can make toast here 90 | } 91 | } 92 | 93 | @Override 94 | public void onProviderEnabled(String provider) 95 | { 96 | //Log.v(TAG,"inside Provider enabled"); 97 | if (provider == LocationManager.GPS_PROVIDER){ 98 | Log.v(TAG,"GPS Provider enabled by user"); 99 | 100 | //can make toast here 101 | } 102 | else if(provider == LocationManager.NETWORK_PROVIDER){ 103 | Log.v(TAG,"NETWORK Provider enabled by user"); 104 | 105 | //can make toast here 106 | } 107 | } 108 | 109 | @Override 110 | public void onStatusChanged(String provider, int status, Bundle extras) 111 | { 112 | //Log.v(TAG,"inside Provider status changed"); 113 | if (provider == LocationManager.GPS_PROVIDER) 114 | { 115 | Log.v(TAG,"GPS Provider status changed"); 116 | if(status == LocationProvider.OUT_OF_SERVICE){ 117 | Log.v(TAG,"GPS Provider has gone out of service"); 118 | } 119 | else if(status == LocationProvider.TEMPORARILY_UNAVAILABLE){ 120 | Log.v(TAG,"GPS Provider is temporarily unavailable"); 121 | } 122 | else if (status == LocationProvider.AVAILABLE){ 123 | Log.v(TAG,"GPS Provider is available again"); 124 | } 125 | 126 | } 127 | else if(provider == LocationManager.NETWORK_PROVIDER){ 128 | Log.v(TAG,"Network Provider status changed"); 129 | if(status == LocationProvider.OUT_OF_SERVICE){ 130 | Log.v(TAG,"Network Provider has gone out of service"); 131 | } 132 | else if(status == LocationProvider.TEMPORARILY_UNAVAILABLE){ 133 | Log.v(TAG,"Network Provider is temporarily unavailable"); 134 | } 135 | else if (status == LocationProvider.AVAILABLE){ 136 | Log.v(TAG,"Network Provider is available again"); 137 | } 138 | 139 | } 140 | } 141 | 142 | @Override 143 | public IBinder onBind(Intent arg0) 144 | { 145 | return null; 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /app/src/main/java/edu/buffalo/cse/ubwins/cellmon/MainActivity.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Gautam on 6/18/16. 3 | * MBP111.0138.B16 4 | * agautam2@buffalo.edu 5 | * University at Buffalo, The State University of New York. 6 | * Copyright © 2016 Gautam. All rights reserved. 7 | * 8 | * CelNetMon v1.0 ~ gets location pure network based location does not fail to GPS 9 | * CelNetMon v1.1 ~ registers device uses a JSON POST 10 | * CelNetMon v1.2 ~ with user permissions for android v6.0+, records DataActivity and DataSate, logs call state 11 | * CelNetMon v1.2.1 ~ with alarm and periodic recording on 60 secs. 12 | * CelNetMon v1.2.2 ~ Permissions handled on onCreate in MainActivity. GPS functionality included. Records data even when location object returns null. 13 | * CelNetMon v1.3 ~ Uses ScheduledExecutorService, does not uses Alarm(removed), does not uses Handler(removed), Uses wake lock. 14 | * CelNetMon v1.3.1 ~ New Registration Fields Added. Now a total of 19 fields in the registration POST. 15 | * CelNetMon v1.3.2 ~ Registration fields total of 18. HTTP POST via building ByteArrayEntity. Protocol Buffers tested. 16 | * CelNetMon v1.3.3 ~ SALT(SharedPref) and HASH(SHA256) functionality added and tested. Minor Layout Change. Toast Messages Changed. 17 | * CelNetMon v1.4.0 ~ Automated Registration. Splash Screen Added. 18 | * CelNetMon v1.4.1 ~ BETA v1 : Uploading on wifi and charging in foreground, button changes, 19 | */ 20 | 21 | package edu.buffalo.cse.ubwins.cellmon; 22 | 23 | import android.Manifest; 24 | import android.app.Activity; 25 | import android.app.Fragment; 26 | import android.app.FragmentManager; 27 | import android.content.Context; 28 | import android.content.Intent; 29 | import android.content.IntentFilter; 30 | import android.content.SharedPreferences; 31 | import android.content.pm.PackageManager; 32 | import android.content.res.Configuration; 33 | import android.database.Cursor; 34 | import android.database.sqlite.SQLiteDatabase; 35 | import android.net.ConnectivityManager; 36 | import android.net.NetworkInfo; 37 | import android.os.AsyncTask; 38 | import android.os.Build; 39 | import android.preference.PreferenceManager; 40 | import android.support.annotation.NonNull; 41 | import android.support.v4.app.ActivityCompat; 42 | import android.support.v4.view.GravityCompat; 43 | import android.support.v4.widget.DrawerLayout; 44 | import android.support.v7.app.ActionBarDrawerToggle; 45 | import android.support.v7.app.AppCompatActivity; 46 | import android.os.Bundle; 47 | import android.support.v7.widget.Toolbar; 48 | import android.telephony.TelephonyManager; 49 | import android.util.Log; 50 | import android.view.Menu; 51 | import android.view.MenuInflater; 52 | import android.view.MenuItem; 53 | import android.view.View; 54 | import android.widget.AdapterView; 55 | import android.widget.ArrayAdapter; 56 | import android.widget.Button; 57 | import android.widget.ListView; 58 | import android.widget.TextView; 59 | import android.widget.Toast; 60 | import android.support.design.widget.Snackbar; 61 | 62 | import org.apache.http.client.ClientProtocolException; 63 | import org.apache.http.client.methods.HttpGet; 64 | import org.apache.http.entity.ByteArrayEntity; 65 | import org.apache.http.util.EntityUtils; 66 | import org.json.JSONException; 67 | import org.json.JSONObject; 68 | import java.io.File; 69 | import java.io.IOException; 70 | import java.io.InputStream; 71 | import org.apache.http.HttpResponse; 72 | import org.apache.http.client.HttpClient; 73 | import org.apache.http.client.methods.HttpPost; 74 | import org.apache.http.impl.client.DefaultHttpClient; 75 | 76 | import java.net.URI; 77 | import java.net.URISyntaxException; 78 | import java.net.URLEncoder; 79 | import java.security.MessageDigest; 80 | import java.security.NoSuchAlgorithmException; 81 | import android.util.Base64; 82 | import android.app.ActivityManager; 83 | 84 | import com.facebook.stetho.Stetho; 85 | //import com.pushlink.android.PushLink; 86 | 87 | public class MainActivity extends AppCompatActivity implements 88 | ActivityCompat.OnRequestPermissionsResultCallback 89 | { 90 | 91 | public final String TAG = "[CELNETMON-ACTIVITY]"; 92 | private DrawerLayout mLayout; 93 | private static final int REQUEST_LOCATION = 0; 94 | private static final int REQUEST_STORAGE = 2; 95 | private static final int REQUEST_PHONE = 1; 96 | private static final int REQUEST_WAKELOCK = 3; 97 | String URL = "http://104.196.177.7:80/aggregator/register/"; 98 | String fileName = "device_registration_details"; 99 | File file; 100 | String responsePhrase; 101 | String reasonPhrase; 102 | JSONObject jsonObject; 103 | String statusPhrase; 104 | String IMEI_TO_POST; 105 | NetworkStateReceiver receiver; 106 | SharedPreferences preferences; 107 | String URL_UPLOAD = "http://104.196.177.7:80/aggregator/upload/"; 108 | 109 | private String[] actions; 110 | // private DrawerLayout mDrawerLayout; 111 | private ListView mDrawerList; 112 | private CharSequence mTitle; 113 | private CharSequence mDrawerTitle; 114 | private ActionBarDrawerToggle mDrawerToggle; 115 | 116 | @Override 117 | protected void onCreate(Bundle savedInstanceState) 118 | { 119 | preferences = PreferenceManager.getDefaultSharedPreferences(this); 120 | 121 | super.onCreate(savedInstanceState); 122 | setContentView(R.layout.activity_base); 123 | 124 | 125 | // Initialize Stetho to allow for viewing database in the Chrome inspector 126 | Stetho.initialize(Stetho.newInitializerBuilder(this) 127 | .enableDumpapp( 128 | Stetho.defaultDumperPluginsProvider(this) 129 | ).enableWebKitInspector( 130 | Stetho.defaultInspectorModulesProvider(this) 131 | ).build()); 132 | 133 | // Toolbar mToolbar = (Toolbar) findViewById(R.id.toolbar); 134 | // setSupportActionBar(mToolbar); 135 | 136 | file = new File(getApplicationContext().getFilesDir(), fileName); 137 | 138 | /*DECLARE EDITOR FOR PERMISSIONS */ 139 | SharedPreferences.Editor editor = preferences.edit(); 140 | 141 | /*First read location permission*/ 142 | if (ActivityCompat.checkSelfPermission(getApplicationContext(), 143 | Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) 144 | { 145 | requestLocationPermission(); 146 | } 147 | else 148 | { 149 | editor.putBoolean("LOCATION", true); 150 | editor.commit(); 151 | } 152 | 153 | /*Second read phone state permission*/ 154 | if (ActivityCompat.checkSelfPermission(getApplicationContext(), 155 | Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) 156 | { 157 | requestPhonePermission(); 158 | } 159 | else 160 | { 161 | editor.putBoolean("PHONE", true); 162 | editor.commit(); 163 | } 164 | 165 | /*Write to Storage permission*/ 166 | if (ActivityCompat.checkSelfPermission(getApplicationContext(), 167 | Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) 168 | { 169 | requestStoragePermission(); 170 | } 171 | else 172 | { 173 | editor.putBoolean("STORAGE", true); 174 | editor.commit(); 175 | } 176 | 177 | /*Wake Lock Permission*/ 178 | 179 | if (ActivityCompat.checkSelfPermission(getApplicationContext(), 180 | Manifest.permission.WAKE_LOCK) != PackageManager.PERMISSION_GRANTED) 181 | { 182 | requestWakeLockPermission(); 183 | } 184 | else 185 | { 186 | editor.putBoolean("WAKELOCK", true); 187 | editor.commit(); 188 | } 189 | 190 | /* CALL TO REGISTER DEVICE*/ 191 | /*FETCH ALL CONDITIONS TO CHECK FROM SHAREDPREF*/ 192 | boolean locPermission = preferences.getBoolean("LOCATION",false); 193 | boolean storagePermission = preferences.getBoolean("STORAGE",false); 194 | boolean phonePermission = preferences.getBoolean("PHONE",false); 195 | boolean isRegistered = preferences.getBoolean("isRegistered", false); 196 | 197 | /*Call only after all 3 permissions are granted*/ 198 | if(!isRegistered && locPermission && storagePermission && phonePermission) 199 | { 200 | onRegisterClicked(); 201 | } 202 | 203 | Log.v(TAG, "CelNetMon Service Started"); 204 | 205 | 206 | // Set up UI 207 | 208 | mTitle = mDrawerTitle = getTitle(); 209 | actions = getResources().getStringArray(R.array.app_actions); 210 | mLayout = (DrawerLayout) findViewById(R.id.drawer_layout); 211 | mDrawerList = (ListView) findViewById(R.id.left_drawer); 212 | Toolbar mToolbar = (Toolbar) findViewById(R.id.toolbar); 213 | setSupportActionBar(mToolbar); 214 | 215 | mLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START); 216 | 217 | mDrawerList.setAdapter(new ArrayAdapter(this, 218 | R.layout.simple_list_item, actions)); 219 | 220 | mDrawerList.setOnItemClickListener(new DrawerClickListener()); 221 | 222 | 223 | 224 | mDrawerToggle = new ActionBarDrawerToggle( 225 | this, 226 | mLayout, 227 | R.string.navigation_drawer_open, 228 | R.string.navigation_drawer_close 229 | ) { 230 | public void onDrawerClosed(View view){ 231 | super.onDrawerClosed(view); 232 | getSupportActionBar().setTitle(mTitle); 233 | invalidateOptionsMenu(); 234 | } 235 | 236 | public void onDrawerOpened(View drawerView){ 237 | super.onDrawerOpened(drawerView); 238 | getSupportActionBar().setTitle("Options"); 239 | invalidateOptionsMenu(); 240 | } 241 | }; 242 | mDrawerToggle.setDrawerIndicatorEnabled(true); 243 | mLayout.addDrawerListener(mDrawerToggle); 244 | 245 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 246 | getSupportActionBar().setHomeButtonEnabled(true); 247 | 248 | if(savedInstanceState == null){ 249 | selectItem(0); 250 | } 251 | 252 | } 253 | 254 | @Override 255 | protected void onPostCreate(Bundle savedInstanceState){ 256 | super.onPostCreate(savedInstanceState); 257 | mDrawerToggle.syncState(); 258 | } 259 | 260 | @Override 261 | public void onConfigurationChanged(Configuration newConfig){ 262 | super.onConfigurationChanged(newConfig); 263 | mDrawerToggle.onConfigurationChanged(newConfig); 264 | } 265 | 266 | @Override 267 | public boolean onCreateOptionsMenu(Menu menu) { 268 | getMenuInflater().inflate(R.menu.app_menu,menu); 269 | return true; 270 | } 271 | 272 | @Override 273 | public boolean onOptionsItemSelected(MenuItem item) { 274 | if(mDrawerToggle.onOptionsItemSelected(item)){ 275 | return true; 276 | } 277 | 278 | return super.onOptionsItemSelected(item); 279 | } 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | public void requestLocationPermission() { 292 | ActivityCompat.requestPermissions(this, 293 | new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_LOCATION); 294 | } 295 | 296 | public void requestPhonePermission() { 297 | ActivityCompat.requestPermissions(this, 298 | new String[]{Manifest.permission.READ_PHONE_STATE}, REQUEST_PHONE); 299 | } 300 | 301 | public void requestStoragePermission() { 302 | ActivityCompat.requestPermissions(this, 303 | new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_STORAGE); 304 | } 305 | 306 | public void requestWakeLockPermission() { 307 | ActivityCompat.requestPermissions(this, 308 | new String[]{Manifest.permission.WAKE_LOCK}, REQUEST_WAKELOCK); 309 | } 310 | 311 | @Override 312 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, 313 | @NonNull int[] grantResults) { 314 | SharedPreferences.Editor editor = preferences.edit(); 315 | if (requestCode == REQUEST_LOCATION) { 316 | /*Check if the only required permission has been granted*/ 317 | if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { 318 | //Log.i(TAG, "Location permission has now been granted."); 319 | Snackbar.make(mLayout, R.string.permission_available_location, 320 | Snackbar.LENGTH_SHORT).show(); 321 | editor.putBoolean("LOCATION", true); 322 | editor.commit(); 323 | boolean one = preferences.getBoolean("STORAGE", false); 324 | boolean two = preferences.getBoolean("PHONE", false); 325 | boolean isRegistered = preferences.getBoolean("isRegistered", false); 326 | if(one && two && !isRegistered) 327 | { 328 | onRegisterClicked(); 329 | } 330 | } 331 | else { 332 | Snackbar.make(mLayout, R.string.permissions_not_granted, 333 | Snackbar.LENGTH_SHORT).show(); 334 | } 335 | } 336 | else if (requestCode == REQUEST_STORAGE) { 337 | if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { 338 | Snackbar.make(mLayout, R.string.permission_available_storage, 339 | Snackbar.LENGTH_SHORT).show(); 340 | editor.putBoolean("STORAGE", true); 341 | editor.commit(); 342 | boolean one = preferences.getBoolean("LOCATION", false); 343 | boolean two = preferences.getBoolean("PHONE", false); 344 | boolean isRegistered = preferences.getBoolean("isRegistered", false); 345 | if(one && two && !isRegistered) 346 | { 347 | onRegisterClicked(); 348 | } 349 | } 350 | else { 351 | Snackbar.make(mLayout, R.string.permissions_not_granted, 352 | Snackbar.LENGTH_SHORT).show(); 353 | } 354 | } 355 | else if (requestCode == REQUEST_PHONE) { 356 | if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { 357 | Snackbar.make(mLayout, R.string.permission_available_phone, 358 | Snackbar.LENGTH_SHORT).show(); 359 | editor.putBoolean("PHONE", true); 360 | editor.commit(); 361 | boolean one = preferences.getBoolean("LOCATION", false); 362 | boolean two = preferences.getBoolean("STORAGE", false); 363 | boolean isRegistered = preferences.getBoolean("isRegistered", false); 364 | if(one && two && !isRegistered) 365 | { 366 | onRegisterClicked(); 367 | } 368 | } else { 369 | Snackbar.make(mLayout, R.string.permissions_not_granted, 370 | Snackbar.LENGTH_SHORT).show(); 371 | } 372 | } 373 | else if (requestCode == REQUEST_WAKELOCK) { 374 | if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { 375 | Snackbar.make(mLayout, R.string.permission_available_wakelock, 376 | Snackbar.LENGTH_SHORT).show(); 377 | editor.putBoolean("WAKELOCK", true); 378 | editor.commit(); 379 | } 380 | else { 381 | Snackbar.make(mLayout, R.string.permissions_not_granted, 382 | Snackbar.LENGTH_SHORT).show(); 383 | } 384 | } 385 | else { 386 | super.onRequestPermissionsResult(requestCode, permissions, grantResults); 387 | } 388 | } 389 | 390 | /*Methods to get registration fields*/ 391 | /* https://developer.android.com/reference/android/os/Build.html */ 392 | 393 | 394 | private String getIMEI() { 395 | TelephonyManager telephonyManager = 396 | (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); 397 | return telephonyManager.getDeviceId(); 398 | } 399 | 400 | private String getBoard() { return Build.BOARD; } 401 | private String getBrand() { return Build.BRAND; } 402 | private String getDevice() { return Build.DEVICE; } 403 | private String getHardware() { return android.os.Build.HARDWARE; } 404 | private String getManufacturer() { return android.os.Build.MANUFACTURER; } 405 | private String getModel() { return android.os.Build.MODEL; } 406 | private String getProduct() { return Build.PRODUCT; } 407 | 408 | /* https://developer.android.com/reference/android/telephony/TelephonyManager.html */ 409 | 410 | private String getNetworkCountryISO() { 411 | TelephonyManager telephonyManager = 412 | (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); 413 | return telephonyManager.getNetworkCountryIso(); 414 | } 415 | 416 | private String getNetworkOperatorCode() { 417 | TelephonyManager telephonyManager = 418 | (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); 419 | return telephonyManager.getNetworkOperator(); 420 | } 421 | 422 | private String getNetworkOperatorName() { 423 | TelephonyManager telephonyManager = 424 | (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); 425 | return telephonyManager.getNetworkOperatorName(); 426 | } 427 | 428 | private String getPhoneType() { 429 | TelephonyManager telephonyManager = 430 | (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); 431 | return Integer.toString(telephonyManager.getPhoneType()); 432 | } 433 | 434 | private String getSimCountryISO() { 435 | TelephonyManager telephonyManager = 436 | (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); 437 | return telephonyManager.getSimCountryIso(); 438 | } 439 | 440 | private String getSimOperator() { 441 | TelephonyManager telephonyManager = 442 | (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); 443 | return telephonyManager.getSimOperator(); 444 | } 445 | 446 | private String getSimOperatorName() { 447 | TelephonyManager telephonyManager = 448 | (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); 449 | return telephonyManager.getSimOperatorName(); 450 | } 451 | 452 | /* https://developer.android.com/reference/android/os/Build.VERSION.html */ 453 | //private String getBaseOS(){return Build.VERSION.BASE_OS;} 454 | 455 | private String getRelease() { return Build.VERSION.RELEASE; } 456 | private String getSdkInt() { return Integer.toString(Build.VERSION.SDK_INT); } 457 | 458 | public boolean isConnected() 459 | { 460 | ConnectivityManager connMgr = 461 | (ConnectivityManager) getSystemService(Activity.CONNECTIVITY_SERVICE); 462 | NetworkInfo networkInfo = connMgr.getActiveNetworkInfo(); 463 | return networkInfo != null && networkInfo.isConnected(); 464 | } 465 | 466 | 467 | public String POST(String url) 468 | { 469 | Log.d(TAG, "Registering device..."); 470 | 471 | InputStream inputStream = null; 472 | int statusCode; 473 | String result = ""; 474 | 475 | try { 476 | 477 | String IMEI = getIMEI(); 478 | IMEI_TO_POST = genHash(IMEI); 479 | 480 | 481 | /*FETCH OTHER PARAMS*/ 482 | String board = getBoard(); 483 | String brand = getBrand(); 484 | String device = getDevice(); 485 | String hardware = getHardware(); 486 | String manufacturer = getManufacturer(); 487 | String modelMake = getModel(); 488 | String product = getProduct(); 489 | 490 | String networkCountryISO = getNetworkCountryISO(); 491 | String networkOperatorCode = getNetworkOperatorCode(); 492 | String networkOperatorName = getNetworkOperatorName(); 493 | String phoneType = getPhoneType(); 494 | String simCountryISO = getSimCountryISO(); 495 | String simOperator = getSimOperator(); 496 | String simOperatorName = getSimOperatorName(); 497 | 498 | String release = getRelease(); 499 | String sdkInt = getSdkInt(); 500 | 501 | /*SERIALIZATION*/ 502 | RegisterDeviceOuterClass.RegisterDevice registerDevice = 503 | RegisterDeviceOuterClass.RegisterDevice.newBuilder() 504 | .setIMEIHASH(IMEI_TO_POST) 505 | .setBOARD(board) 506 | .setBRAND(brand) 507 | .setDEVICE(device) 508 | .setHARDWARE(hardware) 509 | .setMANUFACTURER(manufacturer) 510 | .setMODEL(modelMake) 511 | .setPRODUCT(product) 512 | .setNETWORKCOUNTRYISO(networkCountryISO) 513 | .setNETWORKOPERATORCODE(networkOperatorCode) 514 | .setNETWORKOPERATORNAME(networkOperatorName) 515 | .setPHONETYPE(phoneType) 516 | .setSIMCOUNTRYISO(simCountryISO) 517 | .setSIMOPERATOR(simOperator) 518 | .setSIMOPERATORNAME(simOperatorName) 519 | .setRELEASE(release) 520 | .setSDKINT(sdkInt).build(); 521 | 522 | byte infoToSend[] = registerDevice.toByteArray(); 523 | 524 | /*1. create HttpClient*/ 525 | HttpClient httpclient = new DefaultHttpClient(); 526 | 527 | /*2. make POST request to the given URL*/ 528 | HttpPost httpPost = new HttpPost(url); 529 | 530 | /*5. Build ByteArrayEntity*/ 531 | //StringEntity se = new StringEntity(json); 532 | ByteArrayEntity byteArrayEntity = new ByteArrayEntity(infoToSend); 533 | 534 | /*6. Set httpPost Entity*/ 535 | httpPost.setEntity(byteArrayEntity); 536 | //httpPost.setEntity(inputStreamEntity); 537 | 538 | /*7. Set some headers to inform server about the type of the content*/ 539 | //httpPost.setHeader("Accept", "application/json"); 540 | //httpPost.setHeader("Content-type", "application/json"); 541 | 542 | /*8. Execute POST request to the given URL*/ 543 | HttpResponse httpResponse = httpclient.execute(httpPost); 544 | 545 | /*9. receive response as inputStream*/ 546 | statusCode = httpResponse.getStatusLine().getStatusCode(); 547 | 548 | /*CONVERT INPUT STREAM TO STRING*/ 549 | responsePhrase = EntityUtils.toString(httpResponse.getEntity()); 550 | Log.v(TAG, "RESPONSE" + responsePhrase); 551 | Log.d(TAG, httpResponse.toString()); 552 | /*PARSE JSON RESPONSE*/ 553 | 554 | if(statusCode!=404) 555 | { 556 | result = Integer.toString(statusCode); 557 | Log.v(TAG, "STATUS CODE: " + result); 558 | } 559 | else 560 | { 561 | result = Integer.toString(statusCode); 562 | //Log.v(TAG, "STATUS CODE: " + result); 563 | } 564 | } 565 | catch (Exception e) 566 | { 567 | Log.d("InputStream", e.getLocalizedMessage()); 568 | } 569 | return result; 570 | } 571 | 572 | public void onRegisterClicked() 573 | { 574 | boolean isConnected = isConnected(); 575 | boolean isRegistered = preferences.getBoolean("isRegistered", false); 576 | if (isConnected && !isRegistered) 577 | { 578 | new HttpAsyncTask().execute(URL); 579 | } 580 | else 581 | { 582 | Log.v(TAG, "isConnected = FALSE"); 583 | Toast.makeText(getBaseContext(), 584 | "Device has no Internet Connectivity! " + 585 | "Please check your Network Connection and try again", 586 | Toast.LENGTH_LONG).show(); 587 | 588 | if(!isRegistered) 589 | { 590 | 591 | receiver = new NetworkStateReceiver(); 592 | receiver.addListener(new NetworkStateReceiver.NetworkStateReceiverListener() { 593 | @Override 594 | public void networkAvailable() { 595 | onRegisterClicked(); 596 | } 597 | 598 | @Override 599 | public void networkUnavailable() { 600 | //Log.v("AUTOMATE", "NETWORK IS UNAVAILABLE"); 601 | } 602 | }); 603 | registerReceiver(receiver,new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)); 604 | } 605 | } 606 | } 607 | 608 | 609 | private class HttpAsyncTask extends AsyncTask 610 | { 611 | @Override 612 | protected String doInBackground(String... urls) 613 | { 614 | return POST(urls[0]); 615 | } 616 | @Override 617 | protected void onPostExecute(String result) 618 | { 619 | try { 620 | jsonObject = new JSONObject(responsePhrase); 621 | statusPhrase = jsonObject.getString("status"); 622 | } 623 | catch (JSONException j) 624 | { 625 | j.printStackTrace(); 626 | } 627 | 628 | if(statusPhrase!=null && statusPhrase.equals("SUCCESS")) 629 | { 630 | TextView textView = (TextView) findViewById(R.id.textView30); 631 | if(textView != null) textView.setText("Device Registered!"); 632 | SharedPreferences.Editor editor = preferences.edit(); 633 | editor.putBoolean("isRegistered", true); 634 | editor.commit(); 635 | boolean temp = preferences.getBoolean("isRegistered", false); 636 | Log.e(TAG, "isRegistered value [temp]: " + temp); 637 | } 638 | else if(statusPhrase!=null && statusPhrase.equals("FAIL")) 639 | { 640 | try { 641 | reasonPhrase = jsonObject.getString("reason"); 642 | } 643 | catch (JSONException j) 644 | { 645 | j.printStackTrace(); 646 | } 647 | if(reasonPhrase.equals("Given IMEI_HASH configuration already exists")) 648 | { 649 | TextView textView = (TextView) findViewById(R.id.textView30); 650 | if(textView != null) textView.setText("Device Already Registered!"); 651 | SharedPreferences.Editor editor = preferences.edit(); 652 | editor.putBoolean("isRegistered", true); 653 | editor.commit(); 654 | } 655 | else 656 | { 657 | Log.d(TAG, reasonPhrase); 658 | TextView textView = (TextView) findViewById(R.id.textView30); 659 | if(textView != null) textView.setText("Device Registration Failed!"); 660 | } 661 | } 662 | } 663 | } 664 | 665 | private String genHash(String input) throws NoSuchAlgorithmException 666 | { 667 | String IMEI_Base64=""; 668 | try 669 | { 670 | MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); 671 | byte[] sha256Hash = sha256.digest(input.getBytes("UTF-8")); 672 | IMEI_Base64 = Base64.encodeToString(sha256Hash, Base64.DEFAULT); 673 | IMEI_Base64=IMEI_Base64.replaceAll("\n", ""); 674 | } 675 | catch(Exception e) 676 | { 677 | e.printStackTrace(); 678 | } 679 | return IMEI_Base64; 680 | } 681 | 682 | private class DrawerClickListener implements android.widget.AdapterView.OnItemClickListener { 683 | @Override 684 | public void onItemClick(AdapterView parent, View view, int position, long id) { 685 | Log.d(TAG,"ID selected: " + id); 686 | Log.d(TAG, "ID for Home: " + R.string.app_menu_home); 687 | selectItem(position); 688 | } 689 | } 690 | 691 | private void selectItem(int position){ 692 | Fragment fragment; 693 | switch (position){ 694 | case 0: 695 | fragment = new HomeFragment(); 696 | break; 697 | case 1: 698 | fragment = new MapFragment(); 699 | break; 700 | default: 701 | fragment = new HomeFragment(); 702 | } 703 | FragmentManager fragmentManager = getFragmentManager(); 704 | fragmentManager.beginTransaction() 705 | .replace(R.id.content_frame, fragment) 706 | .commit(); 707 | 708 | mDrawerList.setItemChecked(position, true); 709 | setTitle(actions[position]); 710 | mLayout.closeDrawers(); 711 | } 712 | 713 | @Override 714 | public void setTitle(CharSequence title){ 715 | mTitle = title; 716 | getSupportActionBar().setTitle(mTitle); 717 | } 718 | } 719 | -------------------------------------------------------------------------------- /app/src/main/java/edu/buffalo/cse/ubwins/cellmon/MapFragment.java: -------------------------------------------------------------------------------- 1 | package edu.buffalo.cse.ubwins.cellmon; 2 | 3 | import android.app.DatePickerDialog; 4 | import android.app.Fragment; 5 | import android.content.Context; 6 | import android.database.Cursor; 7 | import android.database.DatabaseUtils; 8 | import android.database.sqlite.SQLiteDatabase; 9 | import android.os.AsyncTask; 10 | import android.os.Bundle; 11 | import android.telephony.TelephonyManager; 12 | import android.util.Base64; 13 | import android.util.Log; 14 | import android.view.LayoutInflater; 15 | import android.view.Menu; 16 | import android.view.MenuInflater; 17 | import android.view.MenuItem; 18 | import android.view.View; 19 | import android.view.ViewGroup; 20 | import android.widget.DatePicker; 21 | import android.widget.Toast; 22 | 23 | import com.google.android.gms.maps.CameraUpdateFactory; 24 | import com.google.android.gms.maps.GoogleMap; 25 | import com.google.android.gms.maps.MapView; 26 | import com.google.android.gms.maps.MapsInitializer; 27 | import com.google.android.gms.maps.OnMapReadyCallback; 28 | import com.google.android.gms.maps.model.BitmapDescriptor; 29 | import com.google.android.gms.maps.model.BitmapDescriptorFactory; 30 | import com.google.android.gms.maps.model.CameraPosition; 31 | import com.google.android.gms.maps.model.LatLng; 32 | import com.google.android.gms.maps.model.MarkerOptions; 33 | import com.google.maps.android.clustering.ClusterManager; 34 | 35 | import org.apache.http.HttpResponse; 36 | import org.apache.http.NameValuePair; 37 | import org.apache.http.client.ClientProtocolException; 38 | import org.apache.http.client.HttpClient; 39 | import org.apache.http.client.methods.HttpGet; 40 | import org.apache.http.client.utils.URLEncodedUtils; 41 | import org.apache.http.impl.client.DefaultHttpClient; 42 | import org.apache.http.message.BasicNameValuePair; 43 | import org.apache.http.util.EntityUtils; 44 | import org.json.JSONArray; 45 | import org.json.JSONException; 46 | import org.json.JSONObject; 47 | import org.json.JSONStringer; 48 | 49 | import java.io.IOException; 50 | import java.lang.reflect.Array; 51 | import java.net.URI; 52 | import java.net.URISyntaxException; 53 | import java.net.URLEncoder; 54 | import java.security.MessageDigest; 55 | import java.security.NoSuchAlgorithmException; 56 | import java.text.ParseException; 57 | import java.text.SimpleDateFormat; 58 | import java.util.ArrayList; 59 | import java.util.Calendar; 60 | import java.util.Date; 61 | import java.util.LinkedList; 62 | import java.util.List; 63 | import java.util.TimeZone; 64 | 65 | import static android.R.attr.data; 66 | import static android.R.attr.fragment; 67 | import static android.os.Build.VERSION_CODES.M; 68 | import static com.facebook.stetho.inspector.network.PrettyPrinterDisplayType.JSON; 69 | 70 | import static edu.buffalo.cse.ubwins.cellmon.LocationFinder.longitude; 71 | 72 | /** 73 | * Created by pcoonan on 3/15/17. 74 | */ 75 | 76 | public class MapFragment extends Fragment implements DateSelectedListener { 77 | public final String TAG = "[CELNETMON-MAPFRAG]"; 78 | 79 | MapView mMapView; 80 | private GoogleMap googleMap; 81 | private float[] colorMap = { 0.0f, 30.0f, 60.0f, 90.0f, 120.0f }; 82 | private String[] networkTypeMap = { "GSM", "CDMA", "LTE", "WCDMA" }; 83 | private Long mindate; 84 | 85 | public MapFragment(){ 86 | setHasOptionsMenu(true); 87 | } 88 | 89 | @Override 90 | public void onCreate(Bundle savedInstanceState){ 91 | super.onCreate(savedInstanceState); 92 | new MinDateTask().execute("start"); 93 | setHasOptionsMenu(true); 94 | } 95 | @Override 96 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 97 | View rootView = inflater.inflate(R.layout.location_fragment, container, false); 98 | 99 | mMapView = (MapView) rootView.findViewById(R.id.mapView); 100 | mMapView.onCreate(savedInstanceState); 101 | 102 | mMapView.onResume(); // needed to get the map to display immediately 103 | 104 | try { 105 | MapsInitializer.initialize(getActivity().getApplicationContext()); 106 | } catch (Exception e) { 107 | e.printStackTrace(); 108 | } 109 | 110 | mMapView.getMapAsync(new OnMapReadyCallback() { 111 | @Override 112 | public void onMapReady(GoogleMap mMap) { 113 | googleMap = mMap; 114 | refreshMap(null); 115 | } 116 | }); 117 | 118 | return rootView; 119 | } 120 | 121 | 122 | 123 | 124 | @Override 125 | public void onResume() { 126 | super.onResume(); 127 | mMapView.onResume(); 128 | } 129 | 130 | @Override 131 | public void onPause() { 132 | super.onPause(); 133 | mMapView.onPause(); 134 | } 135 | 136 | @Override 137 | public void onDestroy() { 138 | super.onDestroy(); 139 | mMapView.onDestroy(); 140 | } 141 | 142 | @Override 143 | public void onLowMemory() { 144 | super.onLowMemory(); 145 | mMapView.onLowMemory(); 146 | } 147 | 148 | @Override 149 | public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){ 150 | inflater.inflate(R.menu.map_menu, menu); 151 | super.onCreateOptionsMenu(menu, inflater); 152 | } 153 | 154 | @Override 155 | public boolean onOptionsItemSelected(MenuItem item) { 156 | switch (item.getItemId()){ 157 | case R.id.day_select: 158 | Log.d(TAG, "Day selected"); 159 | if(!item.isChecked()) item.setChecked(true); 160 | new MinDateTask().execute("day"); 161 | return true; 162 | case R.id.week_select: 163 | Log.d(TAG, "Week selected"); 164 | if(!item.isChecked()) item.setChecked(true); 165 | 166 | new MinDateTask().execute("week"); 167 | return true; 168 | case R.id.month_select: 169 | Log.d(TAG, "Month selected"); 170 | if(!item.isChecked()) item.setChecked(true); 171 | new MinDateTask().execute("month"); 172 | return true; 173 | default: 174 | Log.d(TAG, "Other selected"); 175 | return super.onOptionsItemSelected(item); 176 | } 177 | } 178 | 179 | @Override 180 | public void onFinishSelect(String type, String value) { 181 | Log.d(TAG, type + " " + value); 182 | Calendar minDate = Calendar.getInstance(); 183 | SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy"); 184 | sdf.setTimeZone(TimeZone.getTimeZone("UTC")); 185 | try{ 186 | minDate.setTime(sdf.parse(value)); 187 | } 188 | catch(ParseException e){ 189 | Log.d(TAG, "Parse exception thrown in onFinishSelect"); 190 | } 191 | Log.d(TAG, "Time is: " + minDate.getTimeInMillis()); 192 | value = String.valueOf(minDate.getTimeInMillis()); 193 | new GetJSONTask().execute(type, value); 194 | // refreshMap(); 195 | } 196 | 197 | private void refreshMap(ArrayList entries){ 198 | googleMap.clear(); 199 | 200 | // For showing a move to my location button 201 | googleMap.setMyLocationEnabled(true); 202 | ClusterManager clusterManager = 203 | new ClusterManager(getActivity().getApplicationContext(), googleMap); 204 | // For dropping a marker at a point on the Map 205 | double lat = (ForegroundService.FusedApiLatitude != null) ? 206 | ForegroundService.FusedApiLatitude : -34; 207 | double lon = (ForegroundService.FusedApiLongitude != null) ? 208 | ForegroundService.FusedApiLongitude : 151; 209 | LatLng curLoc = new LatLng(lat, lon); 210 | double latAvg = 0; 211 | double longAvg = 0; 212 | 213 | // Trying a few different approaches below. First, if there are no entries retrieved from 214 | // the server, we resort to using the locally stored data. This was implemented for 215 | // testing the map out, and I would recommend removing this feature to prevent any 216 | // confusion. 217 | 218 | // In the event that there are entries retrieved from the server, we display them. The most 219 | // basic approach to this is just to create standard markers and add some of the entry 220 | // information to them. Given the large amount of data presented, this is not realistic 221 | // for map responsiveness. 222 | 223 | // To fix the issue of displaying large amounts of data, we look to using clustering. There 224 | // are two approaches to this, custom and using the Google Maps Cluster Manager. So far, the 225 | // custom clustering approach is not complete, as it needs much better fine-grained tuning. 226 | // The Cluster Manager takes care of clustering the data points, but we need more control 227 | // over how these clusters are displayed. Currently each cluster shows how many markers are 228 | // contained within, but we want to display the average signal strength to the user. 229 | 230 | 231 | // TODO: 232 | // - Determine if the Cluster Manager can be used to show the data we want 233 | // - Split data between 3G and LTE (add toggle in UI) 234 | // - Filter out stale entries 235 | // - Adjust map zoom to contain all points/clusters (sometimes zooms in too much) 236 | 237 | if(entries == null){ 238 | String rawQuery = "SELECT * FROM mapRecords"; 239 | DBHandler dbHandler = new DBHandler(getActivity().getApplicationContext()); 240 | SQLiteDatabase sqLiteDatabase = dbHandler.getWritableDatabase(); 241 | Cursor cur = sqLiteDatabase.rawQuery(rawQuery, null); 242 | 243 | 244 | double count = DatabaseUtils.queryNumEntries(sqLiteDatabase, "mapRecords"); 245 | 246 | if(cur.moveToFirst()){ 247 | do{ 248 | LatLng temp = new LatLng(cur.getDouble(1), cur.getDouble(2)); 249 | latAvg += (cur.getDouble(1) / count); 250 | longAvg += (cur.getDouble(2) / count); 251 | 252 | // Log.d("LOCDB", "Lat is: " + cur.getDouble(1)); 253 | // Log.d("LOCDB", "Long is: " + cur.getDouble(2)); 254 | googleMap.addMarker( 255 | new MarkerOptions() 256 | .position(temp) 257 | .title(networkTypeMap[cur.getInt(5)]) 258 | .snippet(cur.getInt(11) + " dBm") 259 | .icon(BitmapDescriptorFactory. 260 | defaultMarker(colorMap[cur.getInt(12)]))); 261 | 262 | 263 | } 264 | while(cur.moveToNext()); 265 | } 266 | else{ 267 | googleMap.addMarker(new MarkerOptions().position(curLoc).title("Current Location").snippet("No data")); 268 | } 269 | cur.close(); 270 | sqLiteDatabase.close(); 271 | } 272 | else{ 273 | // Custom cluster method - currently not working correctly 274 | // List clusters = init(entries, 200); 275 | // calculate(clusters, entries); 276 | // 277 | // for(Cluster cluster: clusters){ 278 | // LatLng temp = new LatLng(cluster.centroid.getLatitude(), cluster.centroid.getLongitude()); 279 | // latAvg += (cluster.centroid.getLatitude() / clusters.size()); 280 | // longAvg += (cluster.centroid.getLongitude() / clusters.size()); 281 | // 282 | // googleMap.addMarker( 283 | // new MarkerOptions() 284 | // .position(temp) 285 | // .title("Cluster " + cluster.id) 286 | // .snippet("Number of entries: " + cluster.getEntries().size()) 287 | // ); 288 | // } 289 | 290 | 291 | for(Entry e: entries){ 292 | // LatLng temp = new LatLng(e.coordinate.getLatitude(), e.coordinate.getLongitude()); 293 | latAvg += (e.coordinate.getLatitude() / entries.size()); 294 | longAvg += (e.coordinate.getLongitude() / entries.size()); 295 | // For Cluster Manager approach 296 | clusterManager.addItem(e); 297 | 298 | // Uncomment for basic marker approach 299 | // googleMap.addMarker( 300 | // new MarkerOptions() 301 | // .position(temp) 302 | // .title(networkTypeMap[e.networkCellType]) 303 | // .snippet(e.dbm + " dBm") 304 | // .icon(BitmapDescriptorFactory. 305 | // defaultMarker(colorMap[e.signalLevel]))); 306 | } 307 | 308 | // Only needed for Cluster Manager 309 | googleMap.setOnCameraIdleListener(clusterManager); 310 | googleMap.setOnMarkerClickListener(clusterManager); 311 | } 312 | 313 | 314 | if(latAvg != 0 && longAvg != 0) curLoc = new LatLng(latAvg, longAvg); 315 | // For zooming automatically to the location of the marker 316 | CameraPosition cameraPosition = new CameraPosition.Builder().target(curLoc).zoom(12).build(); 317 | googleMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition)); 318 | } 319 | 320 | private ArrayList init(List entries, int numClusters){ 321 | Log.d(TAG, "Intitializing K-means"); 322 | ArrayList clusters= new ArrayList(); 323 | double minLat, minLong; 324 | minLat = minLong = Double.MAX_VALUE; 325 | double maxLat, maxLong; 326 | maxLat = maxLong = Double.MAX_VALUE * -1; 327 | 328 | for(Entry e: entries){ 329 | minLat = Math.min(minLat, e.coordinate.getLatitude()); 330 | minLong = Math.min(minLong, e.coordinate.getLongitude()); 331 | maxLat = Math.max(maxLat, e.coordinate.getLatitude()); 332 | maxLong = Math.max(maxLong, e.coordinate.getLongitude()); 333 | // Log.d(TAG, "Max long: " + maxLong + " entry long: " + e.coordinate.getLongitude()); 334 | } 335 | 336 | Log.d(TAG, "Min latitude: " + minLat + " max latitude: " + maxLat); 337 | Log.d(TAG, "Min longitude: " + minLong + " max longitude " + maxLong); 338 | for(int i = 0; i < numClusters; ++i){ 339 | Cluster cluster = new Cluster(i); 340 | Coordinate centroid = Coordinate.createRandomCoordinate(maxLat, minLat, minLong, maxLong); 341 | cluster.setCentroid(centroid); 342 | clusters.add(cluster); 343 | } 344 | 345 | Log.d(TAG, "K-means initialization complete"); 346 | return clusters; 347 | } 348 | 349 | private void calculate(List clusters, List entries){ 350 | Log.d(TAG, "K-means calculation beginning..."); 351 | boolean finish = false; 352 | int iteration = 0; 353 | 354 | while(!finish){ 355 | clearClusters(clusters); 356 | 357 | List lastCentroids = getCentroids(clusters); 358 | 359 | assignCluster(entries, clusters); 360 | 361 | calculateCentroids(clusters); 362 | 363 | iteration++; 364 | 365 | List currentCentroids = getCentroids(clusters); 366 | 367 | double distance = 0; 368 | for(int i = 0; i < lastCentroids.size(); ++i){ 369 | distance += Coordinate.distance(lastCentroids.get(i), currentCentroids.get(i)); 370 | } 371 | 372 | if(distance == 0){ 373 | finish = true; 374 | } 375 | if(iteration % 100 == 0){ 376 | Log.d(TAG, "Iteration: " + iteration); 377 | } 378 | } 379 | 380 | Log.d(TAG, "K-means calculating complete."); 381 | } 382 | 383 | private void clearClusters(List clusters){ 384 | for(Cluster cluster: clusters){ 385 | cluster.clear(); 386 | } 387 | } 388 | 389 | private List getCentroids(List clusters){ 390 | List centroids = new ArrayList(); 391 | for(Cluster cluster: clusters){ 392 | Coordinate aux = cluster.getCentroid(); 393 | Coordinate coordinate = new Coordinate(aux.getLatitude(),aux.getLongitude()); 394 | centroids.add(coordinate); 395 | } 396 | return centroids; 397 | } 398 | 399 | private void assignCluster(List entries, List clusters){ 400 | double max = Double.MAX_VALUE; 401 | double min = max; 402 | int cluster = 0; 403 | double distance = 0.0; 404 | 405 | for(Entry entry: entries){ 406 | min = max; 407 | for(int i = 0; i < clusters.size(); ++i){ 408 | Cluster c = clusters.get(i); 409 | distance = Coordinate.distance(entry.coordinate, c.getCentroid()); 410 | if(distance < min){ 411 | min = distance; 412 | cluster = i; 413 | } 414 | } 415 | entry.clusterNumber = cluster; 416 | clusters.get(cluster).addEntry(entry); 417 | } 418 | } 419 | 420 | private void calculateCentroids(List clusters){ 421 | for(Cluster cluster: clusters){ 422 | double sumLat = 0; 423 | double sumLong = 0; 424 | List entries = cluster.getEntries(); 425 | int npoints = entries.size(); 426 | 427 | for(Entry entry: entries){ 428 | sumLat += entry.coordinate.getLatitude(); 429 | sumLong += entry.coordinate.getLongitude(); 430 | } 431 | 432 | Coordinate centroid = cluster.getCentroid(); 433 | if(npoints > 0){ 434 | double newLat = sumLat / npoints; 435 | double newLong = sumLong / npoints; 436 | centroid.setLatitude(newLat); 437 | centroid.setLongitude(newLong); 438 | } 439 | } 440 | } 441 | 442 | // Task that gets the earliest date for a user. 443 | // TODO: 444 | // - Get maximum date as well. 445 | // - Basic error handling 446 | 447 | class MinDateTask extends AsyncTask { 448 | private String type; 449 | @Override 450 | protected Long doInBackground(String... params) { 451 | type = params[0]; 452 | if(mindate != null) return mindate; 453 | String IMEI_HASH = ""; 454 | String responseStr = ""; 455 | Long ret = System.currentTimeMillis(); 456 | try { 457 | /*HASH IMEI*/ 458 | IMEI_HASH = genHash(getIMEI()); 459 | } 460 | catch (NoSuchAlgorithmException e) { 461 | e.printStackTrace(); 462 | } 463 | HttpResponse response = null; 464 | try { 465 | HttpClient client = new DefaultHttpClient(); 466 | HttpGet request = new HttpGet(); 467 | String customURL = "http://104.196.177.7/aggregator/mindate?imei_hash=" 468 | + URLEncoder.encode(IMEI_HASH, "UTF-8"); 469 | // Log.d(TAG, customURL); 470 | request.setURI(new URI(customURL)); 471 | response = client.execute(request); 472 | 473 | responseStr = EntityUtils.toString(response.getEntity()); 474 | Log.v(TAG, "RESPONSE" + responseStr); 475 | 476 | /*PARSE JSON RESPONSE*/ 477 | JSONObject jsonObject = new JSONObject(responseStr); 478 | String status = jsonObject.getString("status"); 479 | if(status.equals("SUCCESS")){ 480 | String time = jsonObject.getString("timestamp"); 481 | 482 | SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss"); 483 | sdf.setTimeZone(TimeZone.getTimeZone("UTC")); 484 | 485 | long timestamp = Long.parseLong(time); 486 | String parsed = sdf.format(new Date(timestamp)); 487 | Log.d(TAG, "Mindate: " + parsed); 488 | ret = timestamp; 489 | } 490 | } catch (URISyntaxException | IOException | JSONException e) { 491 | e.printStackTrace(); 492 | } 493 | return ret; 494 | } 495 | 496 | @Override 497 | protected void onPostExecute(Long timestamp) { 498 | switch (type){ 499 | case "day": 500 | Log.d(TAG, "Timestamp retrieved for day"); 501 | DatePickerFragment fragment = DatePickerFragment.newInstance(timestamp); //new DatePickerFragment(); 502 | fragment.setTargetFragment(MapFragment.this, 0); 503 | fragment.show(getActivity().getFragmentManager(), "datePicker"); 504 | break; 505 | case "week": 506 | Log.d(TAG, "Timestamp retrieved for week"); 507 | WeekPickerFragment weekPickerFragment = WeekPickerFragment.newInstance(timestamp); 508 | weekPickerFragment.setTargetFragment(MapFragment.this, 1); 509 | weekPickerFragment.show(getActivity().getFragmentManager(), "weekPicker"); 510 | break; 511 | case "month": 512 | Log.d(TAG, "Timestamp retrieved for month"); 513 | MonthPickerFragment monthPickerFragment = MonthPickerFragment.newInstance(timestamp); 514 | monthPickerFragment.setTargetFragment(MapFragment.this, 2); 515 | monthPickerFragment.show(getActivity().getFragmentManager(), "monthPicker"); 516 | break; 517 | case "start": 518 | Log.d(TAG, "Timestamp cached."); 519 | mindate = timestamp; 520 | break; 521 | default: 522 | Log.d(TAG, "Other selected"); 523 | // return super.onOptionsItemSelected(item); 524 | } 525 | } 526 | } 527 | 528 | class GetJSONTask extends AsyncTask{ 529 | private ArrayList entryList; 530 | public GetJSONTask(){ 531 | entryList = new ArrayList(); 532 | } 533 | 534 | @Override 535 | protected Boolean doInBackground(String... strings) { 536 | String timespan = strings[0]; 537 | String timestart = strings[1]; 538 | Boolean ret = false; 539 | 540 | String IMEI_HASH = ""; 541 | String responseStr = ""; 542 | try { 543 | /*HASH IMEI*/ 544 | IMEI_HASH = genHash(getIMEI()); 545 | } 546 | catch (NoSuchAlgorithmException e) { 547 | e.printStackTrace(); 548 | } 549 | HttpResponse response = null; 550 | try { 551 | HttpClient client = new DefaultHttpClient(); 552 | HttpGet request = new HttpGet(); 553 | // String customURL = "http://104.196.177.7/aggregator/genjson?imei_hash=" 554 | // + URLEncoder.encode(IMEI_HASH, "UTF-8"); 555 | String customURL = "http://104.196.177.7/aggregator/genjson?"; 556 | List params = new LinkedList(); 557 | params.add(new BasicNameValuePair("imei_hash", IMEI_HASH)); 558 | params.add(new BasicNameValuePair("timespan", timespan)); 559 | params.add(new BasicNameValuePair("timestart", timestart)); 560 | String paramString = URLEncodedUtils.format(params, "utf-8"); 561 | 562 | customURL += paramString; 563 | 564 | // Log.d(TAG, customURL); 565 | request.setURI(new URI(customURL)); 566 | response = client.execute(request); 567 | 568 | responseStr = EntityUtils.toString(response.getEntity()); 569 | Log.v(TAG, "JSON received."); 570 | 571 | 572 | /*PARSE JSON RESPONSE*/ 573 | JSONObject jsonObject = new JSONObject(responseStr); 574 | String status = jsonObject.getString("status"); 575 | if(status.equals("SUCCESS")){ 576 | JSONObject data = jsonObject.getJSONObject("data"); 577 | JSONArray entries = data.getJSONArray("entries"); 578 | Log.d(TAG, entries.length() + " entries for selected timeframe"); 579 | for(int i = 0; i < entries.length(); ++i){ 580 | JSONObject jsonentry = entries.getJSONObject(i); 581 | // Log.d(TAG, entry.toString()); 582 | Entry entry = Entry.mapJSON(jsonentry); 583 | entryList.add(entry); 584 | } 585 | ret = true; 586 | } 587 | 588 | } catch (URISyntaxException | IOException | JSONException e) { 589 | e.printStackTrace(); 590 | } 591 | return ret; 592 | } 593 | 594 | @Override 595 | protected void onPostExecute(Boolean b){ 596 | if(b){ 597 | refreshMap(entryList); 598 | } 599 | else{ 600 | Log.d(TAG, "No data for selected date"); 601 | // Add popup alert 602 | } 603 | } 604 | } 605 | 606 | private String genHash(String input) throws NoSuchAlgorithmException 607 | { 608 | String IMEI_Base64=""; 609 | try 610 | { 611 | MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); 612 | byte[] sha256Hash = sha256.digest(input.getBytes("UTF-8")); 613 | IMEI_Base64 = Base64.encodeToString(sha256Hash, Base64.DEFAULT); 614 | IMEI_Base64=IMEI_Base64.replaceAll("\n", ""); 615 | } 616 | catch(Exception e) 617 | { 618 | e.printStackTrace(); 619 | } 620 | return IMEI_Base64; 621 | } 622 | 623 | private String getIMEI() { 624 | TelephonyManager telephonyManager = 625 | (TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE); 626 | return telephonyManager.getDeviceId(); 627 | } 628 | } -------------------------------------------------------------------------------- /app/src/main/java/edu/buffalo/cse/ubwins/cellmon/MonthPickerFragment.java: -------------------------------------------------------------------------------- 1 | package edu.buffalo.cse.ubwins.cellmon; 2 | 3 | import android.app.AlertDialog; 4 | import android.app.Dialog; 5 | import android.app.DialogFragment; 6 | import android.content.DialogInterface; 7 | import android.os.Bundle; 8 | import android.util.Log; 9 | 10 | import java.text.SimpleDateFormat; 11 | import java.util.ArrayList; 12 | import java.util.Calendar; 13 | import java.util.HashMap; 14 | 15 | import static android.R.attr.minDate; 16 | 17 | /** 18 | * Created by pcoonan on 4/6/17. 19 | */ 20 | 21 | public class MonthPickerFragment extends DialogFragment { 22 | 23 | public final String TAG = "[CELNETMON-MONTHFRAG]"; 24 | private static int selectedItem = 0; 25 | private static HashMap monthMap = new HashMap<>(); 26 | public final String[] months = {"January", "February", "March", 27 | "April", "May", "June", 28 | "July", "August", "September", 29 | "October", "November", "December"}; 30 | 31 | public static MonthPickerFragment newInstance(long mindate){ 32 | MonthPickerFragment fragment = new MonthPickerFragment(); 33 | 34 | Bundle args = new Bundle(); 35 | args.putLong("mindate", mindate); 36 | fragment.setArguments(args); 37 | 38 | return fragment; 39 | } 40 | 41 | @Override 42 | public Dialog onCreateDialog(Bundle savedInstanceState){ 43 | Long mindate = getArguments().getLong("mindate"); 44 | 45 | AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getActivity()); 46 | alertDialogBuilder.setTitle("Select month"); 47 | 48 | Calendar minDate = Calendar.getInstance(); 49 | minDate.setTimeInMillis(mindate); 50 | 51 | // Add a month to have the first full month of data 52 | minDate.add(Calendar.MONTH, 1); 53 | minDate.set(Calendar.DAY_OF_MONTH, 1); 54 | 55 | Calendar maxDate = Calendar.getInstance(); 56 | maxDate.add(Calendar.DATE, -2); 57 | maxDate.add(Calendar.MONTH, -1); 58 | ArrayList monthList = new ArrayList(); 59 | 60 | int month = maxDate.get(Calendar.MONTH); 61 | int year = maxDate.get(Calendar.YEAR); 62 | int i = 0; 63 | // for(int i = 0; i < 24; ++i){ 64 | while(maxDate.after(minDate)){ 65 | monthList.add(months[month] + " " + year); 66 | monthMap.put(i, month + "/01/" + year); 67 | i++; 68 | maxDate.add(Calendar.MONTH, -1); 69 | 70 | month = maxDate.get(Calendar.MONTH); 71 | year = maxDate.get(Calendar.YEAR); 72 | } 73 | 74 | final CharSequence[] finalMonths = new CharSequence[monthList.size()]; 75 | int index = 0; 76 | for(String mon : monthList){ 77 | finalMonths[index++] = mon; 78 | } 79 | alertDialogBuilder.setSingleChoiceItems(finalMonths, selectedItem, new DialogInterface.OnClickListener() { 80 | @Override 81 | public void onClick(DialogInterface dialog, int which) { 82 | selectedItem = which; 83 | sendBackResult(); 84 | } 85 | }); 86 | return alertDialogBuilder.create(); 87 | } 88 | 89 | public void sendBackResult(){ 90 | DateSelectedListener listener = (DateSelectedListener) getTargetFragment(); 91 | listener.onFinishSelect("month", monthMap.get(selectedItem)); 92 | dismiss(); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /app/src/main/java/edu/buffalo/cse/ubwins/cellmon/NetworkStateReceiver.java: -------------------------------------------------------------------------------- 1 | package edu.buffalo.cse.ubwins.cellmon; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.net.ConnectivityManager; 7 | import android.net.NetworkInfo; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | /** 13 | * Created by AmmY on 01/09/16. 14 | */ 15 | public class NetworkStateReceiver extends BroadcastReceiver 16 | { 17 | 18 | static List listeners; 19 | protected Boolean connected; 20 | public final String TAG = "[NW-STATE-RCVR]"; 21 | 22 | 23 | 24 | public NetworkStateReceiver() 25 | { 26 | listeners = new ArrayList<>(); 27 | connected = null; 28 | } 29 | 30 | 31 | public static int TYPE_WIFI = 1; 32 | public static int TYPE_MOBILE = 2; 33 | public static int TYPE_NOT_CONNECTED = 0; 34 | 35 | 36 | public static int getConnectivityStatus(Context context) 37 | { 38 | ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 39 | 40 | NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); 41 | if (null != activeNetwork) 42 | { 43 | if(activeNetwork.getType() == ConnectivityManager.TYPE_WIFI) 44 | return TYPE_WIFI; 45 | 46 | if(activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) 47 | return TYPE_MOBILE; 48 | } 49 | return TYPE_NOT_CONNECTED; 50 | } 51 | 52 | public static String getConnectivityStatusString(Context context) 53 | { 54 | int conn = getConnectivityStatus(context); 55 | String status = null; 56 | if (conn == TYPE_WIFI) 57 | { 58 | status = "Wifi enabled"; 59 | } 60 | else if (conn == TYPE_MOBILE) 61 | { 62 | status = "Mobile data enabled"; 63 | } 64 | else if (conn == TYPE_NOT_CONNECTED) 65 | { 66 | status = "Not connected to Internet"; 67 | } 68 | return status; 69 | } 70 | 71 | @Override 72 | public void onReceive(Context context, Intent intent) 73 | { 74 | if (intent == null || intent.getExtras() == null) 75 | return; 76 | 77 | ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 78 | NetworkInfo ni = manager.getActiveNetworkInfo(); 79 | 80 | if (ni != null && ni.getState() == NetworkInfo.State.CONNECTED) 81 | { 82 | connected = true; 83 | } 84 | else if (intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, Boolean.FALSE)) 85 | { 86 | connected = false; 87 | } 88 | 89 | notifyStateToAll(); 90 | } 91 | 92 | private void notifyStateToAll() 93 | { 94 | for (NetworkStateReceiverListener listener : listeners) { 95 | //Log.e("NSR","Listener "); 96 | notifyState(listener); 97 | } 98 | } 99 | 100 | private void notifyState(NetworkStateReceiverListener listener) 101 | { 102 | if (connected == null || listener == null) 103 | return; 104 | 105 | if (connected == true) 106 | listener.networkAvailable(); 107 | else 108 | listener.networkUnavailable(); 109 | 110 | } 111 | 112 | public void addListener(NetworkStateReceiverListener l) 113 | { 114 | listeners.add(l); 115 | notifyState(l); 116 | } 117 | 118 | public void removeListener(NetworkStateReceiverListener l) 119 | { 120 | listeners.remove(l); 121 | } 122 | 123 | public interface NetworkStateReceiverListener 124 | { 125 | void networkAvailable(); 126 | void networkUnavailable(); 127 | } 128 | } -------------------------------------------------------------------------------- /app/src/main/java/edu/buffalo/cse/ubwins/cellmon/PhoneCallState.java: -------------------------------------------------------------------------------- 1 | package edu.buffalo.cse.ubwins.cellmon; 2 | 3 | /** 4 | * Created by Gautam on 7/3/16. 5 | * MBP111.0138.B16 6 | * agautam2@buffalo.edu 7 | * University at Buffalo, The State University of New York. 8 | * Copyright © 2016 Gautam. All rights reserved. 9 | */ 10 | 11 | import java.util.Date; 12 | 13 | import android.content.BroadcastReceiver; 14 | import android.content.Context; 15 | import android.content.Intent; 16 | import android.telephony.TelephonyManager; 17 | 18 | public class PhoneCallState extends BroadcastReceiver 19 | { 20 | private static int lastState = TelephonyManager.CALL_STATE_IDLE; 21 | private static Date callStartTime; 22 | private static boolean isIncoming; 23 | private static String savedNumber; //because the passed incoming is only valid in ringing 24 | 25 | 26 | @Override 27 | public void onReceive(Context context, Intent intent) 28 | { 29 | //Log.d("onReceive", "onReceive: inside onReceive "); 30 | if (intent.getAction().equals("android.intent.action.NEW_OUTGOING_CALL")) 31 | 32 | { 33 | //Log.d("onReceive", "onReceive: inside onReceive of outgoing call "); 34 | savedNumber = intent.getExtras().getString("android.intent.extra.PHONE_NUMBER"); 35 | } 36 | else 37 | { 38 | String stateStr = intent.getExtras().getString(TelephonyManager.EXTRA_STATE); 39 | String number = intent.getExtras().getString(TelephonyManager.EXTRA_INCOMING_NUMBER); 40 | int state = 0; 41 | if(stateStr.equals(TelephonyManager.EXTRA_STATE_IDLE)) 42 | { 43 | state = TelephonyManager.CALL_STATE_IDLE; 44 | } 45 | else if(stateStr.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) 46 | { 47 | state = TelephonyManager.CALL_STATE_OFFHOOK; 48 | } 49 | else if(stateStr.equals(TelephonyManager.EXTRA_STATE_RINGING)) 50 | { 51 | state = TelephonyManager.CALL_STATE_RINGING; 52 | } 53 | 54 | onCallStateChanged(context, state, number); 55 | } 56 | 57 | } 58 | 59 | //TODO Override 60 | protected void onIncomingCallStarted(Context ctx, String number, Date start){} 61 | protected void onOutgoingCallStarted(Context ctx, String number, Date start){} 62 | protected void onIncomingCallEnded(Context ctx, String number, Date start, Date end){} 63 | protected void onOutgoingCallEnded(Context ctx, String number, Date start, Date end){} 64 | protected void onMissedCall(Context ctx, String number, Date start){} 65 | 66 | 67 | //Incoming call- goes from IDLE to RINGING when it rings, to OFFHOOK when it's answered, to IDLE when its hung up 68 | //Outgoing call- goes from IDLE to OFFHOOK when it dials out, to IDLE when hung up 69 | public void onCallStateChanged(Context context, int state, String number) 70 | { 71 | //Log.v("TAG","state is "+ state); 72 | if(lastState == state) 73 | { 74 | return; 75 | } 76 | switch (state) 77 | { 78 | case TelephonyManager.CALL_STATE_RINGING: 79 | isIncoming = true; 80 | callStartTime = new Date(); 81 | savedNumber = number; 82 | //Log.v("TAG","Ringing"); 83 | break; 84 | case TelephonyManager.CALL_STATE_OFFHOOK: 85 | //Log.v("TAG","Offhook"); 86 | //Transition of ringing->offhook are pickups of incoming calls. Nothing done on them 87 | if(lastState != TelephonyManager.CALL_STATE_RINGING) 88 | { 89 | isIncoming = false; 90 | callStartTime = new Date(); 91 | //Log.v("TAG","Call started outgoing"); 92 | onOutgoingCallStarted(context, savedNumber, callStartTime); 93 | } 94 | if(lastState == TelephonyManager.CALL_STATE_RINGING) 95 | { 96 | //Log.v("TAG","Picked up"); 97 | onIncomingCallStarted(context, number, callStartTime); 98 | } 99 | break; 100 | case TelephonyManager.CALL_STATE_IDLE: 101 | //Went to idle- this is the end of a call. What type depends on previous state(s) 102 | if(lastState == TelephonyManager.CALL_STATE_RINGING) 103 | { 104 | //Ring but no pickup- a miss 105 | //Log.v("TAG","Missed"); 106 | onMissedCall(context, savedNumber, callStartTime); 107 | } 108 | else if(isIncoming) 109 | { 110 | onIncomingCallEnded(context, savedNumber, callStartTime, new Date()); 111 | } 112 | else 113 | { 114 | onOutgoingCallEnded(context, savedNumber, callStartTime, new Date()); 115 | } 116 | break; 117 | } 118 | lastState = state; 119 | } 120 | 121 | } 122 | -------------------------------------------------------------------------------- /app/src/main/java/edu/buffalo/cse/ubwins/cellmon/PhoneCallStateRecorder.java: -------------------------------------------------------------------------------- 1 | package edu.buffalo.cse.ubwins.cellmon; 2 | 3 | import android.content.Context; 4 | 5 | import java.util.Date; 6 | 7 | /** 8 | * Created by Gautam on 7/3/16. 9 | * MBP111.0138.B16 10 | * agautam2@buffalo.edu 11 | * University at Buffalo, The State University of New York. 12 | * Copyright © 2016 Gautam. All rights reserved. 13 | */ 14 | public class PhoneCallStateRecorder extends PhoneCallState 15 | { 16 | public static int call_state = 0; 17 | static final String TAG = "[CELNETMON-PCSR]"; 18 | @Override 19 | protected void onIncomingCallStarted(Context ctx, String number, Date start) 20 | { 21 | //Log.v(TAG, "Call Received from: " + number + " at: " + start); 22 | call_state = 1; 23 | } 24 | 25 | @Override 26 | protected void onIncomingCallEnded(Context ctx, String number, Date start, Date end) 27 | { 28 | //Log.v(TAG, "Call Received from: " + number + " at: " + start + " ended at: " + end); 29 | call_state = 0; 30 | } 31 | 32 | @Override 33 | protected void onOutgoingCallStarted(Context ctx, String number, Date start) 34 | { 35 | //Log.v(TAG, "Outgoing call to: " + number + " started at: " + start); 36 | call_state = 1; 37 | } 38 | 39 | @Override 40 | protected void onOutgoingCallEnded(Context ctx, String number, Date start, Date end) 41 | { 42 | //Log.v(TAG, "Outgoing call to: " + number + " started at: " + start + " ended at: " + end); 43 | call_state = 0; 44 | } 45 | 46 | @Override 47 | protected void onMissedCall(Context ctx, String number, Date start) 48 | { 49 | //Log.v(TAG, "Missed call from: " + number + " at: " + start); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/java/edu/buffalo/cse/ubwins/cellmon/ScheduleIntentReceiver.java: -------------------------------------------------------------------------------- 1 | package edu.buffalo.cse.ubwins.cellmon; 2 | 3 | import android.app.Service; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.location.Location; 7 | import android.location.LocationManager; 8 | import android.os.IBinder; 9 | import android.support.v7.app.AppCompatActivity; 10 | import android.telephony.TelephonyManager; 11 | import android.util.Base64; 12 | import android.util.Log; 13 | import android.widget.Toast; 14 | 15 | import org.apache.http.HttpResponse; 16 | import org.apache.http.client.ClientProtocolException; 17 | import org.apache.http.client.HttpClient; 18 | import org.apache.http.client.methods.HttpGet; 19 | import org.apache.http.impl.client.DefaultHttpClient; 20 | 21 | import java.io.IOException; 22 | import java.net.URI; 23 | import java.net.URISyntaxException; 24 | import java.net.URLEncoder; 25 | import java.security.MessageDigest; 26 | import java.security.NoSuchAlgorithmException; 27 | import java.security.Provider; 28 | 29 | 30 | /** 31 | * Created by Gautam on 7/18/16. 32 | * MBP111.0138.B16 33 | * agautam2@buffalo.edu 34 | * University at Buffalo, The State University of New York. 35 | * Copyright © 2016 Gautam. All rights reserved. 36 | */ 37 | public class ScheduleIntentReceiver extends Service { 38 | // LocationFinder locationFinder; 39 | CellularDataRecorder cdr; 40 | PhoneCallStateRecorder pcsr; 41 | DBstore dbStore; 42 | public final String TAG = "[CELNETMON-HNDLRCVR]"; 43 | int keepAlive = 0; 44 | String IMEI_HASH; 45 | String IMEI; 46 | 47 | public void onScheduleIntentReceiver(Context arg0) { 48 | keepAlive++; 49 | 50 | // locationFinder = new LocationFinder(arg0); 51 | 52 | final TelephonyManager telephonyManager = 53 | (TelephonyManager) arg0.getSystemService(Context.TELEPHONY_SERVICE); 54 | IMEI = telephonyManager.getDeviceId(); 55 | cdr = new CellularDataRecorder(); 56 | pcsr = new PhoneCallStateRecorder(); 57 | 58 | // locationFinder = new LocationFinder(arg0); 59 | //Log.v(TAG, "Calling getLocalTimeStamp and getCellularInfo"); 60 | 61 | /*FETCH INFO FROM CDR CLASS*/ 62 | Long timeStamp = cdr.getLocalTimeStamp(); 63 | String cellularInfo = cdr.getCellularInfo(telephonyManager); 64 | int dataActivity = cdr.getCurrentDataActivity(telephonyManager); 65 | int dataState = cdr.getCurrentDataState(telephonyManager); 66 | int mobileNetworkType = cdr.getMobileNetworkType(telephonyManager); 67 | 68 | final LocationManager locationManager = (LocationManager) arg0.getSystemService(LOCATION_SERVICE); 69 | 70 | if (ForegroundService.FusedApiLatitude == null || ForegroundService.FusedApiLongitude == null) { 71 | return; 72 | } 73 | 74 | /*FETCH INFO FROM FUSED API*/ 75 | Double fusedApiLatitude = ForegroundService.FusedApiLatitude; 76 | Double fusedApiLongitude = ForegroundService.FusedApiLongitude; 77 | boolean stale = ((System.currentTimeMillis() - ForegroundService.LastFusedLocation) > 40000) 78 | && dataState == 0 && !locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER); 79 | 80 | Double locationdata[] = {fusedApiLatitude, fusedApiLongitude}; 81 | 82 | 83 | /*FETCH INFO FROM PCSR CLASS*/ 84 | int phoneCallState = PhoneCallStateRecorder.call_state; 85 | // Log.i(TAG, "onReceive: Location data is before inserting "+locationdata[0] +" "+ locationdata[1]); 86 | // 87 | // 88 | // Log.v(TAG, "TIME STAMP: " + timeStamp); 89 | // Log.v(TAG, "CELLULAR INFO: " + cellularInfo); 90 | // Log.v(TAG, "DATA ACTIVITY: " + dataActivity); 91 | // Log.v(TAG, "DATA STATE: " + dataState); 92 | // Log.v(TAG, "MOBILE NETWORK TYPE: " + mobileNetworkType); 93 | 94 | dbStore = new DBstore(arg0); 95 | dbStore.insertIntoDB(locationdata, stale, timeStamp, cellularInfo, dataActivity, dataState, 96 | phoneCallState, mobileNetworkType); 97 | // Log.e(TAG, "KEEPALIVE: " + keepAlive); 98 | // Ping every hour -> 3600 / log frequency 99 | if (keepAlive == 360) { 100 | /*GET IMEI*/ 101 | try { 102 | /*HASH IMEI*/ 103 | IMEI_HASH = genHash(IMEI); 104 | } catch (NoSuchAlgorithmException e) { 105 | e.printStackTrace(); 106 | } 107 | Log.v(TAG, "GENERATED IMEI HASH"); 108 | //TODO KEEP-ALIVE GET 109 | HttpResponse response = null; 110 | try { 111 | HttpClient client = new DefaultHttpClient(); 112 | HttpGet request = new HttpGet(); 113 | String customURL = "http://104.196.177.7/aggregator/ping?imei_hash=" 114 | + URLEncoder.encode(IMEI_HASH, "UTF-8"); 115 | request.setURI(new URI(customURL)); 116 | response = client.execute(request); 117 | Log.v(TAG, "RESPONSE PHRASE FOR HTTP GET: " 118 | + response.getStatusLine().getReasonPhrase()); 119 | Log.v(TAG, "RESPONSE STATUS FOR HTTP GET: " 120 | + response.getStatusLine().getStatusCode()); 121 | } catch (URISyntaxException e) { 122 | e.printStackTrace(); 123 | } catch (ClientProtocolException e) { 124 | e.printStackTrace(); 125 | } catch (IOException e) { 126 | e.printStackTrace(); 127 | } 128 | //RESET KEEPALIVE 129 | keepAlive = 0; 130 | //Log.e(TAG, "KEEPALIVE RESET: " + keepAlive); 131 | } 132 | 133 | } 134 | 135 | 136 | private String genHash(String input) throws NoSuchAlgorithmException { 137 | String IMEI_Base64 = ""; 138 | try { 139 | MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); 140 | byte[] sha256Hash = sha256.digest(input.getBytes("UTF-8")); 141 | IMEI_Base64 = Base64.encodeToString(sha256Hash, Base64.DEFAULT); 142 | IMEI_Base64 = IMEI_Base64.replaceAll("\n", ""); 143 | } catch (Exception e) { 144 | e.printStackTrace(); 145 | } 146 | return IMEI_Base64; 147 | } 148 | 149 | @Override 150 | public IBinder onBind(Intent intent) { 151 | // Used only in case of bound services. 152 | return null; 153 | } 154 | 155 | } 156 | 157 | 158 | -------------------------------------------------------------------------------- /app/src/main/java/edu/buffalo/cse/ubwins/cellmon/Scheduler.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Gautam on 7/26/16. 3 | * MBP111.0138.B16 4 | * agautam2@buffalo.edu 5 | * University at Buffalo, The State University of New York. 6 | * Copyright © 2016 Gautam. All rights reserved. 7 | */ 8 | 9 | package edu.buffalo.cse.ubwins.cellmon; 10 | 11 | import android.content.Context; 12 | import android.util.Log; 13 | import java.util.concurrent.Executors; 14 | import java.util.concurrent.ScheduledExecutorService; 15 | import java.util.concurrent.ScheduledFuture; 16 | import static java.util.concurrent.TimeUnit.*; 17 | 18 | 19 | public class Scheduler 20 | { 21 | ScheduleIntentReceiver scheduleIntentReceiver = new ScheduleIntentReceiver(); 22 | private static final String TAG = "[CELMON-SCHEDULER]"; 23 | final static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); 24 | public static ScheduledFuture beeperHandle=null; 25 | 26 | public void beep(final Context context) 27 | { 28 | final Runnable beeper = new Runnable() 29 | { 30 | public void run() 31 | { 32 | //Log.v(LOG, "BEEP"); 33 | try { 34 | scheduleIntentReceiver.onScheduleIntentReceiver(context); 35 | } 36 | catch (Exception e) 37 | { 38 | Log.e(TAG,"error in executing: It will no longer be run!: " + e.getMessage()); 39 | e.printStackTrace(); 40 | } 41 | } 42 | }; 43 | beeperHandle = scheduler.scheduleAtFixedRate(beeper, 0, 10, SECONDS); 44 | } 45 | 46 | public static void stopScheduler() 47 | { 48 | scheduler.schedule(new Runnable() 49 | { 50 | public void run() 51 | { 52 | beeperHandle.cancel(true); 53 | } 54 | }, 1, SECONDS); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/java/edu/buffalo/cse/ubwins/cellmon/StartMyServiceAtBootReceiver.java: -------------------------------------------------------------------------------- 1 | package edu.buffalo.cse.ubwins.cellmon; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.content.SharedPreferences; 7 | import android.preference.PreferenceManager; 8 | import android.util.Log; 9 | import android.widget.Toast; 10 | 11 | /** 12 | * Created by AmmY on 12/10/16. 13 | */ 14 | 15 | public class StartMyServiceAtBootReceiver extends BroadcastReceiver { 16 | SharedPreferences preferences; 17 | @Override 18 | public void onReceive(Context context, Intent intent) { 19 | preferences = PreferenceManager.getDefaultSharedPreferences(context); 20 | if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) { 21 | if(preferences.getBoolean("TRACKING",false)) { 22 | Intent serviceIntent = new Intent(context, ForegroundService.class); 23 | serviceIntent.setAction("startforeground"); 24 | context.startService(serviceIntent); 25 | } 26 | else{ 27 | Log.v("Restart","Tracking was off"); 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/edu/buffalo/cse/ubwins/cellmon/WeekPickerFragment.java: -------------------------------------------------------------------------------- 1 | package edu.buffalo.cse.ubwins.cellmon; 2 | 3 | import android.app.AlertDialog; 4 | import android.app.DatePickerDialog; 5 | import android.app.Dialog; 6 | import android.app.DialogFragment; 7 | import android.content.DialogInterface; 8 | import android.os.Bundle; 9 | import android.support.annotation.Nullable; 10 | import android.util.Log; 11 | import android.view.LayoutInflater; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | import android.view.WindowManager; 15 | import android.widget.EditText; 16 | 17 | import java.text.SimpleDateFormat; 18 | import java.util.ArrayList; 19 | import java.util.Calendar; 20 | import java.util.HashMap; 21 | 22 | 23 | 24 | /** 25 | * Created by pcoonan on 4/3/17. 26 | */ 27 | 28 | public class WeekPickerFragment extends DialogFragment { 29 | // private EditText mEditText; 30 | private static int selectedItem = 0; 31 | private static HashMap weekMap = new HashMap<>(); 32 | public final String TAG = "[CELNETMON-WEEKFRAG]"; 33 | 34 | public static WeekPickerFragment newInstance(long mindate){ 35 | WeekPickerFragment fragment = new WeekPickerFragment(); 36 | 37 | Bundle args = new Bundle(); 38 | args.putLong("mindate", mindate); 39 | fragment.setArguments(args); 40 | 41 | return fragment; 42 | } 43 | 44 | 45 | @Override 46 | public Dialog onCreateDialog(Bundle savedInstanceState){ 47 | Long mindate = getArguments().getLong("mindate"); 48 | 49 | AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getActivity()); 50 | alertDialogBuilder.setTitle("Select week"); 51 | 52 | Calendar maxDate = Calendar.getInstance(); 53 | maxDate.add(Calendar.DATE, -7); 54 | 55 | Calendar minDate = Calendar.getInstance(); 56 | minDate.setTimeInMillis(mindate); 57 | 58 | // Get start of week where data was first recorded 59 | minDate.add(Calendar.DAY_OF_WEEK, minDate.getFirstDayOfWeek() - minDate.get(Calendar.DAY_OF_WEEK)); 60 | // Add a week to have the first full week of data 61 | minDate.add(Calendar.DAY_OF_YEAR, 7); 62 | 63 | 64 | SimpleDateFormat df = new SimpleDateFormat("MM/dd/yyyy"); 65 | 66 | Calendar beginWeek = (Calendar) maxDate.clone(); 67 | ArrayList weeks = new ArrayList(); 68 | beginWeek.add(Calendar.DAY_OF_WEEK, beginWeek.getFirstDayOfWeek() - beginWeek.get(Calendar.DAY_OF_WEEK)); 69 | Calendar endWeek = (Calendar) beginWeek.clone(); 70 | endWeek.add(Calendar.DAY_OF_WEEK, 6); 71 | 72 | int day = beginWeek.get(Calendar.DAY_OF_MONTH); 73 | int month = beginWeek.get(Calendar.MONTH); 74 | int year = beginWeek.get(Calendar.YEAR); 75 | // for(int i = 0; i < 104; ++i){ 76 | int i = 0; 77 | while(beginWeek.after(minDate)){ 78 | weeks.add(df.format(beginWeek.getTime()) + "-" + df.format(endWeek.getTime())); 79 | weekMap.put(i, month + "/" + day + "/" + year); 80 | i++; 81 | beginWeek.add(Calendar.DAY_OF_YEAR, -7); 82 | endWeek.add(Calendar.DAY_OF_YEAR, -7); 83 | 84 | day = beginWeek.get(Calendar.DAY_OF_MONTH); 85 | month = beginWeek.get(Calendar.MONTH); 86 | year = beginWeek.get(Calendar.YEAR); 87 | } 88 | 89 | final CharSequence[] finalWeeks = new CharSequence[weeks.size()]; 90 | int index = 0; 91 | for(String week : weeks){ 92 | finalWeeks[index++] = week; 93 | } 94 | alertDialogBuilder.setSingleChoiceItems(finalWeeks, selectedItem, new DialogInterface.OnClickListener() { 95 | @Override 96 | public void onClick(DialogInterface dialog, int which) { 97 | selectedItem = which; 98 | sendBackResult(); 99 | } 100 | }); 101 | return alertDialogBuilder.create(); 102 | } 103 | 104 | public void sendBackResult(){ 105 | DateSelectedListener listener = (DateSelectedListener) getTargetFragment(); 106 | listener.onFinishSelect("week", weekMap.get(selectedItem)); 107 | dismiss(); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /app/src/main/proto/data_record.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option java_package = "edu.buffalo.cse.ubwins.cellmon"; 4 | 5 | message DataEntry { 6 | /* Timestamp */ 7 | uint64 TIMESTAMP = 1; 8 | 9 | /* Location */ 10 | double FUSED_LAT = 2; 11 | double FUSED_LONG = 3; 12 | bool STALE = 4; 13 | 14 | /* https://developer.android.com/reference/android/telephony/TelephonyManager.html */ 15 | enum NetType { 16 | NETWORK_TYPE_UNKNOWN = 0; 17 | NETWORK_TYPE_GPRS = 1; 18 | NETWORK_TYPE_EDGE = 2; 19 | NETWORK_TYPE_UMTS = 3; 20 | NETWORK_TYPE_HSDPA = 4; 21 | NETWORK_TYPE_HSUPA = 5; 22 | NETWORK_TYPE_HSPA = 6; 23 | NETWORK_TYPE_CDMA = 7; 24 | NETWORK_TYPE_EVDO_0 = 8; 25 | NETWORK_TYPE_EVDO_A = 9; 26 | NETWORK_TYPE_EVDO_B = 10; 27 | NETWORK_TYPE_1xRTT = 11; 28 | NETWORK_TYPE_IDEN = 12; 29 | NETWORK_TYPE_LTE = 13; 30 | NETWORK_TYPE_EHRPD = 14; 31 | NETWORK_TYPE_HSPAP = 15; 32 | NETWORK_TYPE_GSM = 16; 33 | NETWORK_TYPE_TD_SCDMA = 17; 34 | NETWORK_TYPE_IWLAN = 18; 35 | } 36 | NetType NETWORK_TYPE = 5; 37 | 38 | /* https://developer.android.com/reference/android/telephony/TelephonyManager.html#getAllCellInfo() */ 39 | enum CellType { 40 | GSM = 0; 41 | CDMA = 1; 42 | WCDMA = 2; 43 | LTE = 3; 44 | } 45 | CellType NETWORK_CELL_TYPE = 6; 46 | 47 | /* Network Parameters: https://developer.android.com/reference/android/telephony/TelephonyManager.html#getAllCellInfo() */ 48 | int32 NETWORK_PARAM_1 = 7; 49 | int32 NETWORK_PARAM_2 = 8; 50 | int32 NETWORK_PARAM_3 = 9; 51 | int32 NETWORK_PARAM_4 = 10; 52 | 53 | /* https://developer.android.com/reference/android/telephony/CellSignalStrength.html */ 54 | int32 SIGNAL_ASU_LEVEL = 11; 55 | sint32 SIGNAL_DBM = 12; 56 | int32 SIGNAL_LEVEL = 13; 57 | 58 | /* https://developer.android.com/reference/android/net/NetworkInfo.DetailedState.html */ 59 | enum NetState { 60 | UNKNOWN = 0; 61 | IDLE = 1; 62 | SCANNING = 2; 63 | CONNECTING = 3; 64 | AUTHENTICATING = 4; 65 | OBTAINING_IPADDR = 5; 66 | CONNECTED = 6; 67 | SUSPENDED = 7; 68 | DISCONNECTING = 8; 69 | DISCONNECTED = 9; 70 | FAILED = 10; 71 | } 72 | NetState NETWORK_STATE = 14; 73 | 74 | /* https://developer.android.com/reference/android/telephony/TelephonyManager.html#getDataActivity() */ 75 | enum DataActivity { 76 | DATA_ACTIVITY_NONE = 0; 77 | DATA_ACTIVITY_IN = 1; 78 | DATA_ACTIVITY_OUT = 2; 79 | DATA_ACTIVITY_INOUT = 3; 80 | DATA_ACTIVITY_DORMANT = 4; 81 | DATA_ACTIVITY_UNKNOWN = 5; 82 | } 83 | DataActivity NETWORK_DATA_ACTIVITY = 15; 84 | 85 | /* https://developer.android.com/reference/android/telephony/TelephonyManager.html */ 86 | enum VoiceCallActivity { 87 | CALL_STATE_IDLE = 0; 88 | CALL_STATE_RINGING = 1; 89 | CALL_STATE_OFFHOOK = 2; 90 | } 91 | VoiceCallActivity VOICE_CALL_STATE = 16; 92 | } 93 | 94 | message DataRecord { 95 | string IMEI_HASH = 1; 96 | string NETWORK_OPERATOR_CODE = 2; 97 | string NETWORK_OPERATOR_NAME = 3; 98 | 99 | repeated DataEntry ENTRY = 4; 100 | } 101 | -------------------------------------------------------------------------------- /app/src/main/proto/register_device.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option java_package = "edu.buffalo.cse.ubwins.cellmon"; 4 | 5 | message RegisterDevice { 6 | string IMEI_HASH = 1; 7 | 8 | /* https://developer.android.com/reference/android/os/Build.html */ 9 | string BOARD = 2; 10 | string BRAND = 3; 11 | string DEVICE = 4; 12 | string HARDWARE = 5; 13 | string MANUFACTURER = 6; 14 | string MODEL = 7; 15 | string PRODUCT = 8; 16 | 17 | /* https://developer.android.com/reference/android/telephony/TelephonyManager.html */ 18 | string NETWORK_COUNTRY_ISO = 9; 19 | string NETWORK_OPERATOR_CODE = 10; 20 | string NETWORK_OPERATOR_NAME = 11; 21 | string PHONE_TYPE = 12; 22 | string SIM_COUNTRY_ISO = 13; 23 | string SIM_OPERATOR = 14; 24 | string SIM_OPERATOR_NAME = 15; 25 | 26 | /* https://developer.android.com/reference/android/os/Build.VERSION.html */ 27 | string RELEASE = 16; 28 | string SDK_INT = 17; 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/res/anim/move_left_in_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/anim/move_left_out_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/anim/move_right_in_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/anim/move_right_out_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/drawer_shadow.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gautamgitspace/CellularNetworkMonitor/cb9a45d2453c65c1959624975218062a42a1312f/app/src/main/res/drawable-hdpi/drawer_shadow.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_date_range_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gautamgitspace/CellularNetworkMonitor/cb9a45d2453c65c1959624975218062a42a1312f/app/src/main/res/drawable-hdpi/ic_date_range_black_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_date_range_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gautamgitspace/CellularNetworkMonitor/cb9a45d2453c65c1959624975218062a42a1312f/app/src/main/res/drawable-hdpi/ic_date_range_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_drawer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gautamgitspace/CellularNetworkMonitor/cb9a45d2453c65c1959624975218062a42a1312f/app/src/main/res/drawable-hdpi/ic_drawer.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/drawer_shadow.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gautamgitspace/CellularNetworkMonitor/cb9a45d2453c65c1959624975218062a42a1312f/app/src/main/res/drawable-mdpi/drawer_shadow.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_date_range_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gautamgitspace/CellularNetworkMonitor/cb9a45d2453c65c1959624975218062a42a1312f/app/src/main/res/drawable-mdpi/ic_date_range_black_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_date_range_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gautamgitspace/CellularNetworkMonitor/cb9a45d2453c65c1959624975218062a42a1312f/app/src/main/res/drawable-mdpi/ic_date_range_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_drawer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gautamgitspace/CellularNetworkMonitor/cb9a45d2453c65c1959624975218062a42a1312f/app/src/main/res/drawable-mdpi/ic_drawer.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/drawer_shadow.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gautamgitspace/CellularNetworkMonitor/cb9a45d2453c65c1959624975218062a42a1312f/app/src/main/res/drawable-xhdpi/drawer_shadow.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_date_range_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gautamgitspace/CellularNetworkMonitor/cb9a45d2453c65c1959624975218062a42a1312f/app/src/main/res/drawable-xhdpi/ic_date_range_black_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_date_range_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gautamgitspace/CellularNetworkMonitor/cb9a45d2453c65c1959624975218062a42a1312f/app/src/main/res/drawable-xhdpi/ic_date_range_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_drawer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gautamgitspace/CellularNetworkMonitor/cb9a45d2453c65c1959624975218062a42a1312f/app/src/main/res/drawable-xhdpi/ic_drawer.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_date_range_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gautamgitspace/CellularNetworkMonitor/cb9a45d2453c65c1959624975218062a42a1312f/app/src/main/res/drawable-xxhdpi/ic_date_range_black_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_date_range_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gautamgitspace/CellularNetworkMonitor/cb9a45d2453c65c1959624975218062a42a1312f/app/src/main/res/drawable-xxhdpi/ic_date_range_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_date_range_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gautamgitspace/CellularNetworkMonitor/cb9a45d2453c65c1959624975218062a42a1312f/app/src/main/res/drawable-xxxhdpi/ic_date_range_black_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_date_range_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gautamgitspace/CellularNetworkMonitor/cb9a45d2453c65c1959624975218062a42a1312f/app/src/main/res/drawable-xxxhdpi/ic_date_range_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/side_nav_bar.xml: -------------------------------------------------------------------------------- 1 | 4 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_base.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 16 | 23 | 24 | 25 | 29 | 30 | 31 | 32 | 33 | 38 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 20 | 21 | 29 | 30 |