Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature allow cellular network access #1612

Merged
merged 3 commits into from
Sep 26, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Android/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />

<uses-feature android:name="android.hardware.screen.portrait"/>

Expand Down
14 changes: 14 additions & 0 deletions Android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ def versionMinor = 2
def versionPatch = 0
def versionBuild = 4 //bump for dogfood builds, public betas, etc.

//Logging levels
def logLevelVerbose = 2;
def logLevelDebug = 3;
def logLevelInfo = 4;
def logLevelWarn = 5;
def logLevelError = 6;
def logLevelAssert = 7;

android {
compileSdkVersion 22
buildToolsVersion "22.0.1"
Expand All @@ -59,6 +67,9 @@ android {
targetSdkVersion 22
versionCode versionMajor * 100000 + versionMinor * 1000 + versionPatch * 100 + versionBuild
versionName "Tower-v${versionMajor}.${versionMinor}.${versionPatch}"

buildConfigField "boolean", "WRITE_LOG_FILE", "true"
buildConfigField "int", "LOG_FILE_LEVEL", "$logLevelDebug"
}

compileOptions {
Expand Down Expand Up @@ -124,17 +135,20 @@ android {
applicationIdSuffix ".beta"
versionNameSuffix ".beta.${versionBuild}"
resValue "string", "app_title", "Tower Beta"
debuggable true
}

beta {
signingConfig signingConfigs.release
versionNameSuffix ".beta.${versionBuild}"
resValue "string", "app_title", "Tower"
debuggable true
}

release {
signingConfig signingConfigs.release
resValue "string", "app_title", "Tower"
buildConfigField "boolean", "WRITE_LOG_FILE", "false"
}
}
}
Expand Down
53 changes: 52 additions & 1 deletion Android/src/org/droidplanner/android/AppService.kt
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
package org.droidplanner.android

import android.annotation.TargetApi
import android.app.Service
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import android.os.Binder
import android.os.Build
import android.support.v4.content.LocalBroadcastManager
import com.o3dr.services.android.lib.drone.attribute.AttributeEvent
import com.o3dr.services.android.lib.drone.attribute.AttributeEventExtra
import org.droidplanner.android.notifications.NotificationHandler
import org.droidplanner.android.utils.NetworkUtils
import timber.log.Timber

/**
* Created by Fredia Huya-Kouadio on 9/24/15.
Expand All @@ -29,10 +37,14 @@ public class AppService : Service() {
}

private val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
override fun onReceive(context: Context, intent: Intent?) {
when (intent?.action) {
AttributeEvent.STATE_CONNECTED -> {
notificationHandler?.init()

if(NetworkUtils.isOnSoloNetwork(context)){
bringUpCellularNetwork(context)
}
}

AttributeEvent.STATE_DISCONNECTED -> {
Expand All @@ -58,8 +70,13 @@ public class AppService : Service() {
super.onCreate()

val dpApp = application as DroidPlannerApp
dpApp.createFileStartLogging()
val drone = dpApp.drone

if(NetworkUtils.isOnSoloNetwork(applicationContext)){
bringUpCellularNetwork(applicationContext)
}

notificationHandler = NotificationHandler(applicationContext, drone)

if (drone.isConnected) {
Expand All @@ -74,7 +91,41 @@ public class AppService : Service() {
LocalBroadcastManager.getInstance(applicationContext).unregisterReceiver(receiver)

notificationHandler?.terminate()

bringDownCellularNetwork()

val dpApp = application as DroidPlannerApp
dpApp.closeLogFile()
}

override fun onBind(intent: Intent?) = binder

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private fun bringUpCellularNetwork(context: Context) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
return

Timber.i("Setting up cellular network request.")
val connMgr = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager

val builder = NetworkRequest.Builder().addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET).addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)

connMgr.requestNetwork(builder.build(), object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
Timber.d("Setting up process default network: %s", network)
ConnectivityManager.setProcessDefaultNetwork(network)
DroidPlannerApp.setCellularNetworkAvailability(true)
}
})
}

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private fun bringDownCellularNetwork() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
return

Timber.d("Bringing down cellular network access.")
ConnectivityManager.setProcessDefaultNetwork(null)
DroidPlannerApp.setCellularNetworkAvailability(false)
}
}
39 changes: 34 additions & 5 deletions Android/src/org/droidplanner/android/DroidPlannerApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@
import org.droidplanner.android.activities.helpers.BluetoothDevicesActivity;
import org.droidplanner.android.maps.providers.google_map.tiles.mapbox.offline.MapDownloader;
import org.droidplanner.android.proxy.mission.MissionProxy;
import org.droidplanner.android.utils.LogToFileTree;
import org.droidplanner.android.utils.Utils;
import org.droidplanner.android.utils.analytics.GAUtils;
import org.droidplanner.android.utils.file.IO.ExceptionWriter;
import org.droidplanner.android.utils.prefs.DroidPlannerPrefs;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

import io.fabric.sdk.android.Fabric;
import timber.log.Timber;
Expand All @@ -59,6 +61,8 @@ public class DroidPlannerApp extends Application implements DroneListener, Tower
public static final String ACTION_DRONE_EVENT = Utils.PACKAGE_NAME + ".ACTION_DRONE_EVENT";
public static final String EXTRA_DRONE_EVENT = "extra_drone_event";

private static final AtomicBoolean isCellularNetworkOn = new AtomicBoolean(false);

private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Expand Down Expand Up @@ -130,14 +134,12 @@ public void run() {
private LocalBroadcastManager lbm;
private MapDownloader mapDownloader;

private LogToFileTree logToFileTree;

@Override
public void onCreate() {
super.onCreate();

if (!BuildConfig.DEBUG) {
Fabric.with(this, new Crashlytics());
}

final Context context = getApplicationContext();

dpPrefs = new DroidPlannerPrefs(context);
Expand All @@ -163,7 +165,14 @@ public void uncaughtException(Thread thread, Throwable ex) {
GAUtils.startNewSession(context);

if (BuildConfig.DEBUG) {
Timber.plant(new Timber.DebugTree());
if (BuildConfig.WRITE_LOG_FILE) {
logToFileTree = new LogToFileTree();
Timber.plant(logToFileTree);
} else {
Timber.plant(new Timber.DebugTree());
}
} else {
Fabric.with(context, new Crashlytics());
}

final IntentFilter intentFilter = new IntentFilter();
Expand Down Expand Up @@ -383,4 +392,24 @@ public void onDroneServiceInterrupted(String errorMsg) {
if (!TextUtils.isEmpty(errorMsg))
Log.e(TAG, errorMsg);
}

public static void setCellularNetworkAvailability(boolean isAvailable){
isCellularNetworkOn.set(isAvailable);
}

public static boolean isCellularNetworkAvailable(){
return isCellularNetworkOn.get();
}

public void createFileStartLogging() {
if (logToFileTree != null) {
logToFileTree.createFileStartLogging(getApplicationContext());
}
}

public void closeLogFile() {
if(logToFileTree != null) {
logToFileTree.stopLoggingThread();
}
}
}
140 changes: 140 additions & 0 deletions Android/src/org/droidplanner/android/utils/LogToFileTree.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package org.droidplanner.android.utils;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.util.Log;

import org.droidplanner.android.BuildConfig;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;

import timber.log.Timber;

/**
* Timber Tree to log specific log levels to a file
*/
public class LogToFileTree extends Timber.DebugTree {
private static final SimpleDateFormat LOG_DATE_FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US);
private static final SimpleDateFormat FILE_DATE_FORMAT = new SimpleDateFormat("yyyy_MM_dd_HH_mm", Locale.US);

private final LinkedBlockingQueue<String> logQueue = new LinkedBlockingQueue<>();
private PrintStream logOutputFile;
private Thread dequeueThread;
private final AtomicBoolean isRunning = new AtomicBoolean(false);
private final Date date = new Date();

@Override
protected void log(int priority, String tag, String message, Throwable t) {
super.log(priority, tag, message, t);

if (isLoggableToFile(priority)) {
String logOutput = getLogMessage(priority, tag, message);
logQueue.add(logOutput);
}
}

private boolean isLoggableToFile(int priority) {
return priority >= BuildConfig.LOG_FILE_LEVEL;
}

private String getLogMessage(int priority, String tag, String message) {
String priorityShort = getPriorityString(priority);
date.setTime(System.currentTimeMillis());
return String.format("%s %s/%s : %s", LOG_DATE_FORMAT.format(date), priorityShort, tag, message);
}

private String getPriorityString(int priority) {
String priorityString = null;
switch (priority) {
case Log.ASSERT:
priorityString = "ASSERT";
break;
case Log.ERROR:
priorityString = "E";
break;
case Log.WARN:
priorityString = "W";
break;
case Log.INFO:
priorityString = "I";
break;
case Log.DEBUG:
priorityString = "D";
break;
case Log.VERBOSE:
priorityString = "V";
break;
default:
priorityString = "";
break;
}
return priorityString;
}

public void createFileStartLogging(final Context context) {
if (dequeueThread != null && dequeueThread.isAlive()) {
stopLoggingThread();
}

dequeueThread = new Thread(new Runnable() {
public void run() {
PackageInfo pInfo;
String version = "";
try {
pInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
version = pInfo.versionName;
} catch (PackageManager.NameNotFoundException e) {
Timber.w("Failed to get package info");
}

File rootDir = context.getExternalFilesDir(null);
File dir = new File(rootDir, "/log_cat/");
dir.mkdirs();

String fileName = String.format("%s_%s.log", version, FILE_DATE_FORMAT.format(new Date()));
File logFile = new File(dir, fileName);
try {
logOutputFile = new PrintStream(new FileOutputStream(logFile, true));

while (isRunning.get()) {
try {
String message = logQueue.take();
logOutputFile.println(message);
} catch (InterruptedException e) {
Timber.w("Failed to receive message from logQueue");
}
}

} catch (IOException e) {
Timber.w("Failed to open file");
} finally {
isRunning.set(false);
if (logOutputFile != null) {
logOutputFile.close();
}
}
}
});

isRunning.set(true);
dequeueThread.start();
}

public void stopLoggingThread() {
if (dequeueThread != null) {
isRunning.set(false);
dequeueThread.interrupt();
dequeueThread = null;
}
}

}
Loading