Route Android logging to native side and timestamp logs

This commit is contained in:
Aaron Culliney 2019-11-12 09:17:22 -08:00
parent 46b74f65fa
commit 310825e2cc
15 changed files with 181 additions and 76 deletions

View File

@ -30,6 +30,7 @@ import android.widget.Toast;
import org.deadc0de.apple2ix.basic.BuildConfig;
import org.deadc0de.apple2ix.basic.R;
import org.json.JSONObject;
import java.io.IOException;
import java.io.InputStream;
@ -96,6 +97,8 @@ public class Apple2Activity extends AppCompatActivity implements Apple2DiskChoos
private static native void nativeReboot(int resetState);
private static native void nativeLogMessage(String jsonStr);
public final static boolean isNativeBarfed() {
return sNativeBarfed;
}
@ -118,7 +121,7 @@ public class Apple2Activity extends AppCompatActivity implements Apple2DiskChoos
}
super.onCreate(savedInstanceState);
Log.e(TAG, "onCreate()");
logMessage(LogType.ERROR, TAG, "onCreate()");
// placeholder view on initial launch
if (mView == null) {
@ -127,14 +130,14 @@ public class Apple2Activity extends AppCompatActivity implements Apple2DiskChoos
Apple2CrashHandler.getInstance().initializeAndSetCustomExceptionHandler(this);
if (sNativeBarfed) {
Log.e(TAG, "NATIVE BARFED...", sNativeBarfedThrowable);
logMessage(LogType.ERROR, TAG, "NATIVE BARFED : " + sNativeBarfedThrowable.getMessage());
return;
}
int sampleRate = DevicePropertyCalculator.getRecommendedSampleRate(this);
int monoBufferSize = DevicePropertyCalculator.getRecommendedBufferSize(this, /*isStereo:*/false);
int stereoBufferSize = DevicePropertyCalculator.getRecommendedBufferSize(this, /*isStereo:*/true);
Log.d(TAG, "Device sampleRate:" + sampleRate + " mono bufferSize:" + monoBufferSize + " stereo bufferSize:" + stereoBufferSize);
logMessage(LogType.DEBUG, TAG, "Device sampleRate:" + sampleRate + " mono bufferSize:" + monoBufferSize + " stereo bufferSize:" + stereoBufferSize);
String dataDir = Apple2Utils.getDataDir(this);
nativeOnCreate(dataDir, sampleRate, monoBufferSize, stereoBufferSize);
@ -185,7 +188,7 @@ public class Apple2Activity extends AppCompatActivity implements Apple2DiskChoos
Apple2Utils.exposeAPKAssets(Apple2Activity.this);
if (externalStoragePermission) {
Apple2Utils.exposeAPKAssetsToExternal(Apple2Activity.this);
Log.d(TAG, "Finished first time copying #1...");
logMessage(LogType.DEBUG, TAG, "Finished first time copying #1...");
if (!(boolean)Apple2Preferences.getJSONPref(Apple2Preferences.PREF_DOMAIN_INTERFACE, Apple2Preferences.PREF_RELEASE_NOTES, false)) {
Runnable myRunnable = new Runnable() {
@ -232,7 +235,7 @@ public class Apple2Activity extends AppCompatActivity implements Apple2DiskChoos
// perform migration(s) and assets exposure now
Apple2Utils.migrateToExternalStorage(Apple2Activity.this);
Apple2Utils.exposeAPKAssetsToExternal(Apple2Activity.this);
Log.d(TAG, "Finished first time copying #2...");
logMessage(LogType.DEBUG, TAG, "Finished first time copying #2...");
} // else ... we keep nagging on app startup ...
} else {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
@ -253,7 +256,7 @@ public class Apple2Activity extends AppCompatActivity implements Apple2DiskChoos
break;
}
Log.d(TAG, "onResume()");
logMessage(LogType.DEBUG, TAG, "onResume()");
showSplashScreen(/*dismissable:*/true);
if (!mSwitchingToPortrait.get()) {
Apple2CrashHandler.getInstance().checkForCrashes(this); // NOTE : needs to be called again to clean-up
@ -320,10 +323,10 @@ public class Apple2Activity extends AppCompatActivity implements Apple2DiskChoos
if (isEmulationPaused()) {
Apple2Preferences.save(this);
} else {
Log.d(TAG, "Letting native save preferences...");
logMessage(LogType.DEBUG, TAG, "Letting native save preferences...");
}
Log.d(TAG, "onPause()");
logMessage(LogType.DEBUG, TAG, "onPause()");
if (mView != null) {
mView.onPause();
}
@ -415,7 +418,7 @@ public class Apple2Activity extends AppCompatActivity implements Apple2DiskChoos
} catch (InterruptedIOException e) {
/* EINTR, EAGAIN */
} catch (IOException e) {
Log.e(TAG, "OOPS could not load release_notes.txt!", e);
logMessage(LogType.ERROR, TAG, "OOPS could not load release_notes.txt : " + e.getMessage());
} finally {
if (is != null) {
try {
@ -620,4 +623,30 @@ public class Apple2Activity extends AppCompatActivity implements Apple2DiskChoos
}
}.run();
}
public enum LogType {
// Constants match
VERBOSE(2),
DEBUG(3),
INFO(4),
WARN(5),
ERROR(6);
private int type;
LogType(int type) {
this.type = type;
}
}
public static void logMessage(LogType type, String tag, String mesg) {
JSONObject map = new JSONObject();
try {
map.put("type", type.type);
map.put("tag", tag);
map.put("mesg", mesg);
nativeLogMessage(map.toString());
} catch (Exception e) {
Log.e(TAG, "OOPS: " + e.getMessage());
}
}
}

View File

@ -221,24 +221,24 @@ public class Apple2CrashHandler {
// here we assume that the crash data was previously sent via email ... if not then we lost it =P
Log.d(TAG, "Cleaning up crash data ...");
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Cleaning up crash data ...");
int idx = 0;
File[] nativeCrashes = _nativeCrashFiles(activity);
for (File crash : nativeCrashes) {
if (!crash.delete()) {
Log.d(TAG, "Could not unlink crash : " + crash);
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Could not unlink crash : " + crash);
}
File processed = new File(_dumpPath2ProcessedPath(crash.getAbsolutePath()));
if (!processed.delete()) {
Log.d(TAG, "Could not unlink processed : " + processed);
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Could not unlink processed : " + processed);
}
}
File javaCrashFile = _javaCrashFile(activity);
if (!javaCrashFile.delete()) {
Log.d(TAG, "Could not unlink java crash : " + javaCrashFile);
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Could not unlink java crash : " + javaCrashFile);
}
// remove previous log file
@ -339,7 +339,7 @@ public class Apple2CrashHandler {
for (File crash : nativeCrashes) {
String crashPath = crash.getAbsolutePath();
Log.d(TAG, "Processing crash : " + crashPath);
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Processing crash : " + crashPath);
String processedPath = _dumpPath2ProcessedPath(crashPath);
try {
@ -350,7 +350,7 @@ public class Apple2CrashHandler {
StringBuilder crashData = new StringBuilder();
if (!Apple2Utils.readEntireFile(new File(processedPath), crashData)) {
Log.e(TAG, "Error processing crash : " + crashPath);
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Error processing crash : " + crashPath);
}
allCrashData.append(">>>>>>> NATIVE CRASH [").append(crashPath).append("]\n");
allCrashData.append(crashData);
@ -410,9 +410,9 @@ public class Apple2CrashHandler {
StringBuilder javaCrashData = new StringBuilder();
File javaCrashFile = _javaCrashFile(activity);
if (javaCrashFile.exists()) {
Log.d(TAG, "Reading java crashes file");
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Reading java crashes file");
if (!Apple2Utils.readEntireFile(javaCrashFile, javaCrashData)) {
Log.e(TAG, "Error processing java crash : " + javaCrashFileName);
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Error processing java crash : " + javaCrashFileName);
}
}
@ -437,7 +437,7 @@ public class Apple2CrashHandler {
try {
obj = new JSONObject(jsonData.toString());
} catch (JSONException e) {
Log.e(TAG, "Error reading preferences : " + e);
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Error reading preferences : " + e);
}
if (obj != null) {
summary.append("PREFS:\n");
@ -511,7 +511,7 @@ public class Apple2CrashHandler {
} catch (InterruptedIOException ie) {
/* EINTR, EAGAIN ... */
} catch (IOException e) {
Log.e(TAG, "Exception attempting to write data : " + e);
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Exception attempting to write data : " + e);
}
try {
@ -564,7 +564,7 @@ public class Apple2CrashHandler {
} else {
allCrashFile = new File(Apple2Utils.getDataDir(activity), "apple2ix_crash.txt");
}
Log.d(TAG, "Writing all crashes to temp file : " + allCrashFile.getAbsolutePath());
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Writing all crashes to temp file : " + allCrashFile.getAbsolutePath());
return allCrashFile;
}
@ -588,14 +588,14 @@ public class Apple2CrashHandler {
File allCrashFile = _getCrashFile(activity);
Apple2Utils.writeFile(allCrashData, allCrashFile);
if (!allCrashFile.setReadable(true, /*ownerOnly:*/false)) {
Log.d(TAG, "Oops, could not set file data readable!");
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Oops, could not set file data readable!");
}
emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(allCrashFile));
Log.d(TAG, "STARTING CHOOSER FOR EMAIL ...");
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "STARTING CHOOSER FOR EMAIL ...");
activity.startActivity(Intent.createChooser(emailIntent, "Send email"));
Log.d(TAG, "AFTER START ACTIVITY ...");
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "AFTER START ACTIVITY ...");
}

View File

@ -55,7 +55,7 @@ public class Apple2DiskChooserActivity extends AppCompatActivity {
resolver.takePersistableUriPermission(uri, (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION));
pfd = resolver.openFileDescriptor(uri, "rw");
} catch (Throwable t) {
Log.e(TAG, "OOPS, could not get appropriate access to URI ( " + uri + " ) : " + t);
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "OOPS, could not get appropriate access to URI ( " + uri + " ) : " + t);
}
return pfd;
@ -83,7 +83,7 @@ public class Apple2DiskChooserActivity extends AppCompatActivity {
fileName = returnCursor.getString(nameIndex);
} catch (Throwable t) {
Log.e(TAG, "OOPS, could not get filename from URI ( " + uri + " ) : " + t);
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "OOPS, could not get filename from URI ( " + uri + " ) : " + t);
}
return fileName;
@ -118,7 +118,7 @@ public class Apple2DiskChooserActivity extends AppCompatActivity {
boolean ran = b.getBoolean("ran");
if (ran) {
Log.e(TAG, "OOPS, already ran...");
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "OOPS, already ran...");
finish();
return;
}
@ -139,7 +139,7 @@ public class Apple2DiskChooserActivity extends AppCompatActivity {
startActivityForResult(pickIntent, EDIT_REQUEST_CODE);
}
} catch (Throwable t) {
Log.e(TAG, "OOPS : " + t);
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "OOPS : " + t);
setResult(RESULT_CANCELED);
finish();
}

View File

@ -369,7 +369,7 @@ public class Apple2DisksMenu implements Apple2MenuView {
try {
diskArgs.pfd.close();
} catch (IOException ioe) {
Log.e(TAG, "Error attempting to close PFD : " + ioe);
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Error attempting to close PFD : " + ioe);
}
}
diskArgs.pfd = null;
@ -381,7 +381,7 @@ public class Apple2DisksMenu implements Apple2MenuView {
}
} catch (Throwable t) {
Log.d(TAG, "OOPS: " + t);
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "OOPS: " + t);
}
}
@ -739,7 +739,7 @@ public class Apple2DisksMenu implements Apple2MenuView {
@Override
public void onItemClick(AdapterView<?> parent, View view, final int position, long id) {
if (isDirectory[position]) {
Log.d(TAG, "Descending to path : " + filePaths[position]);
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Descending to path : " + filePaths[position]);
if (parentIsRootPath && !new File(filePaths[position]).isAbsolute()) {
pushPathStack(parentDisksDir + File.separator + filePaths[position]);
} else {

View File

@ -108,7 +108,7 @@ public class Apple2JoystickCalibration implements Apple2MenuView {
joystickPollerThread = new Thread() {
@Override
public void run() {
Log.i(TAG, "Starting joystick poll thread...");
Apple2Activity.logMessage(Apple2Activity.LogType.INFO, TAG, "Starting joystick poll thread...");
try {
while (true) {
long cxy = nativePollJoystick();
@ -139,7 +139,7 @@ public class Apple2JoystickCalibration implements Apple2MenuView {
Thread.sleep(100);
}
} catch (Exception e) {
Log.i(TAG, "Stopping joystick poll thread...");
Apple2Activity.logMessage(Apple2Activity.LogType.INFO, TAG, "Stopping joystick poll thread...");
}
}
};

View File

@ -216,7 +216,7 @@ public class Apple2KeyboardSettingsMenu extends Apple2AbstractMenu {
File keyboardDir = new File(Apple2Utils.getDataDir(activity) + File.separator + "keyboards");
files = keyboardDir.listFiles(kbdJsonFilter);
if (files == null) {
Log.e(TAG, "OOPS, could not read keyboard data directory");
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "OOPS, could not read keyboard data directory");
return;
}
}

View File

@ -89,7 +89,7 @@ public class Apple2KeypadChooser implements Apple2MenuView {
}
String asciiStr = asciiRepresentation(mActivity, ascii);
Log.d(TAG, "ascii:'" + asciiStr + "' scancode:" + scancode);
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "ascii:'" + asciiStr + "' scancode:" + scancode);
if (ascii == ' ') {
ascii = Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE;
}

View File

@ -179,7 +179,7 @@ public class Apple2MainMenu {
mainMenuView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Log.d(TAG, "position:" + position + " tapped...");
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "position:" + position + " tapped...");
SETTINGS setting = SETTINGS.values()[position];
setting.handleSelection(Apple2MainMenu.this);
}
@ -337,12 +337,12 @@ public class Apple2MainMenu {
try {
diskArgs.pfd.close(); // at this point diskArgs.pfd !null
} catch (IOException ioe) {
Log.e(TAG, "Error attempting to close PFD : " + ioe);
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Error attempting to close PFD : " + ioe);
}
diskArgs.pfd = null;
} catch (Exception e) {
Log.e(TAG, "OOPS: " + e);
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "OOPS: " + e);
}
return restored;
@ -386,7 +386,7 @@ public class Apple2MainMenu {
pfds[i] = Apple2DiskChooserActivity.openFileDescriptorFromUri(activity, uri);
if (pfds[i] == null) {
Log.e(TAG, "Did not find URI for drive #" + i + " specified in " + SAVE_FILE + " file : " + diskPath);
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Did not find URI for drive #" + i + " specified in " + SAVE_FILE + " file : " + diskPath);
} else {
int fd = pfds[i].getFd();
map.put(fdKeys[i], fd);
@ -394,7 +394,7 @@ public class Apple2MainMenu {
} else {
boolean exists = new File(diskPath).exists();
if (!exists) {
Log.e(TAG, "Did not find path for drive #" + i + " specified in " + SAVE_FILE + " file : " + diskPath);
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Did not find path for drive #" + i + " specified in " + SAVE_FILE + " file : " + diskPath);
}
}
}
@ -407,7 +407,7 @@ public class Apple2MainMenu {
pfds[i].close();
}
} catch (IOException ioe) {
Log.e(TAG, "Error attempting to close PFD #" + i + " : " + ioe);
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Error attempting to close PFD #" + i + " : " + ioe);
}
}
map = new JSONObject(jsonString);

View File

@ -103,7 +103,7 @@ public class Apple2Preferences {
key = menu.getPrefKey();
val = map.get(key);
} catch (JSONException e) {
Log.d(TAG, "Did not find value for domain:" + menu.getPrefDefault() + " key:" + key);
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Did not find value for domain:" + menu.getPrefDefault() + " key:" + key);
}
if (val == null && key != null) {
val = menu.getPrefDefault();
@ -123,7 +123,7 @@ public class Apple2Preferences {
map = _prefDomain(domain);
val = map.get(key);
} catch (JSONException e) {
Log.d(TAG, "Did not find value for domain:" + domain + " key:" + key);
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Did not find value for domain:" + domain + " key:" + key);
}
if (val == null) {
val = defaultVal;
@ -143,17 +143,17 @@ public class Apple2Preferences {
try {
return (float) obj;
} catch (ClassCastException e) {
Log.d(TAG, "could not cast object as float");
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "could not cast object as float");
}
try {
return (float) ((double) obj);
} catch (ClassCastException e) {
Log.d(TAG, "could not cast object as double");
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "could not cast object as double");
}
try {
return (float) ((int) obj);
} catch (ClassCastException e) {
Log.d(TAG, "could not cast object as int");
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "could not cast object as int");
}
return (float) ((long) obj);
}
@ -165,17 +165,17 @@ public class Apple2Preferences {
try {
return (int) obj;
} catch (ClassCastException e) {
Log.d(TAG, "could not cast object as int");
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "could not cast object as int");
}
try {
return (int) ((long) obj);
} catch (ClassCastException e) {
Log.d(TAG, "could not cast object as long");
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "could not cast object as long");
}
try {
return (int) ((float) obj);
} catch (ClassCastException e) {
Log.d(TAG, "could not cast object as float");
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "could not cast object as float");
}
return (int) ((double) obj);
}
@ -249,7 +249,7 @@ public class Apple2Preferences {
jsonArray = (JSONArray) getJSONPref(PREF_DOMAIN_JOYSTICK, "kpAxisRosetteChars", null);
if (jsonArray == null || jsonArray.length() != Apple2KeypadSettingsMenu.ROSETTE_SIZE) {
Log.e(TAG, "Oops, kpAxisRosetteChars is not expected length");
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Oops, kpAxisRosetteChars is not expected length");
} else {
for (int i = 0; i < Apple2KeypadSettingsMenu.ROSETTE_SIZE; i++) {
Apple2KeypadSettingsMenu.KeyTuple tuple = axisRosette.get(i);
@ -259,7 +259,7 @@ public class Apple2Preferences {
jsonArray = (JSONArray) getJSONPref(PREF_DOMAIN_JOYSTICK, "kpAxisRosetteScancodes", null);
if (jsonArray == null || jsonArray.length() != Apple2KeypadSettingsMenu.ROSETTE_SIZE) {
Log.e(TAG, "Oops, kpAxisRosetteScancodes is not expected length");
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Oops, kpAxisRosetteScancodes is not expected length");
} else {
for (int i = 0; i < Apple2KeypadSettingsMenu.ROSETTE_SIZE; i++) {
Apple2KeypadSettingsMenu.KeyTuple tuple = axisRosette.get(i);
@ -333,7 +333,7 @@ public class Apple2Preferences {
StringBuilder jsonString = new StringBuilder();
if (!Apple2Utils.readEntireFile(prefsFile, jsonString)) {
Log.d(TAG, "Oops, could not read JSON file : " + prefsFile);
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Oops, could not read JSON file : " + prefsFile);
}
try {
@ -359,7 +359,7 @@ public class Apple2Preferences {
try {
jsonString = sSettings.toString(2);
} catch (JSONException e) {
Log.w(TAG, "Error attempting to pretty-print JSON : " + e);
Apple2Activity.logMessage(Apple2Activity.LogType.WARN, TAG, "Error attempting to pretty-print JSON : " + e);
ex = e;
jsonString = sSettings.toString();
}

View File

@ -425,7 +425,7 @@ public class Apple2SettingsMenu extends Apple2AbstractMenu {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
Log.d(TAG, "About to NPE : " + str[0].length());
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "About to NPE : " + str[0].length());
}
});
}

View File

@ -61,7 +61,7 @@ public class Apple2Utils {
} catch (InterruptedIOException ie) {
/* EINTR, EAGAIN ... */
} catch (IOException e) {
Log.d(TAG, "Error reading file at path : " + file.toString());
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Error reading file at path : " + file.toString());
}
try {
@ -89,7 +89,7 @@ public class Apple2Utils {
} catch (InterruptedIOException ie) {
/* EINTR, EAGAIN ... */
} catch (IOException e) {
Log.e(TAG, "Exception attempting to write data : " + e);
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Exception attempting to write data : " + e);
}
try {
@ -182,7 +182,7 @@ public class Apple2Utils {
if (!externalDir.exists()) {
boolean made = externalDir.mkdirs();
if (!made) {
Log.d(TAG, "WARNING: could not make directory : " + sExternalFilesDir);
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "WARNING: could not make directory : " + sExternalFilesDir);
break;
}
}
@ -218,7 +218,7 @@ public class Apple2Utils {
PackageInfo pi = pm.getPackageInfo(activity.getPackageName(), 0);
sDataDir = pi.applicationInfo.dataDir;
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "" + e);
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "" + e);
if (sDataDir == null) {
sDataDir = "/data/local/tmp";
}
@ -285,7 +285,7 @@ public class Apple2Utils {
recursivelyDelete(new File(new File(sDataDir, "disks").getAbsolutePath(), "logo"));
recursivelyDelete(new File(new File(sDataDir, "disks").getAbsolutePath(), "miscgame"));
Log.d(TAG, "First time copying stuff-n-things out of APK for ease-of-NDK access...");
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "First time copying stuff-n-things out of APK for ease-of-NDK access...");
getExternalStorageDirectory(activity);
recursivelyCopyAPKAssets(activity, /*from APK directory:*/"disks", /*to location:*/new File(sDataDir, "disks").getAbsolutePath(), true);
@ -321,7 +321,7 @@ public class Apple2Utils {
}
}
if (!file.delete()) {
Log.d(TAG, "Failed to delete file: " + file);
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Failed to delete file: " + file);
}
}
@ -338,7 +338,7 @@ public class Apple2Utils {
} catch (InterruptedIOException e) {
/* EINTR, EAGAIN ... */
} catch (IOException e) {
Log.d(TAG, "OOPS exception attempting to list APK files at : " + srcFileOrDir + " : " + e);
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "OOPS exception attempting to list APK files at : " + srcFileOrDir + " : " + e);
}
try {
@ -350,7 +350,7 @@ public class Apple2Utils {
} while (attempts < maxAttempts);
if (files == null) {
Log.d(TAG, "OOPS, could not list APK assets at : " + srcFileOrDir);
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "OOPS, could not list APK assets at : " + srcFileOrDir);
return;
}
@ -359,7 +359,7 @@ public class Apple2Utils {
File dstPath = new File(dstFileOrDir);
if (!dstPath.mkdirs()) {
if (!dstPath.exists()) {
Log.d(TAG, "OOPS, could not mkdirs on " + dstPath);
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "OOPS, could not mkdirs on " + dstPath);
return;
}
}
@ -387,7 +387,7 @@ public class Apple2Utils {
} catch (InterruptedIOException e) {
/* EINTR, EAGAIN */
} catch (IOException e) {
Log.e(TAG, "Failed to copy asset file: " + srcFileOrDir, e);
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Failed to copy asset file: " + srcFileOrDir + " : " + e.getMessage());
} finally {
if (is != null) {
try {
@ -462,7 +462,7 @@ public class Apple2Utils {
}
}
} catch (Exception e) {
Log.e(TAG, "OOPS : {e}");
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "OOPS : {e}");
}
}
@ -478,7 +478,7 @@ public class Apple2Utils {
} catch (InterruptedIOException e) {
// EINTR, EAGAIN ...
} catch (IOException e) {
Log.d(TAG, "OOPS exception attempting to copy emulator state file : " + e);
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "OOPS exception attempting to copy emulator state file : " + e);
}
try {

View File

@ -136,7 +136,7 @@ class Apple2View extends GLSurfaceView implements InputManagerCompat.InputDevice
private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) {
Log.w(TAG, "creating OpenGL ES 2.0 context");
Apple2Activity.logMessage(Apple2Activity.LogType.WARN, TAG, "creating OpenGL ES 2.0 context");
checkEglError("Before eglCreateContext", egl);
int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE};
EGLContext context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list);
@ -152,7 +152,7 @@ class Apple2View extends GLSurfaceView implements InputManagerCompat.InputDevice
private static void checkEglError(String prompt, EGL10 egl) {
int error;
while ((error = egl.eglGetError()) != EGL10.EGL_SUCCESS) {
Log.e(TAG, String.format("%s: EGL error: 0x%x", prompt, error));
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, String.format("%s: EGL error: 0x%x", prompt, error));
}
}
@ -204,9 +204,9 @@ class Apple2View extends GLSurfaceView implements InputManagerCompat.InputDevice
// Now return the "best" one
EGLConfig best = chooseConfig(egl, display, configs);
if (best == null) {
Log.e(TAG, "OOPS! Did not pick an EGLConfig. What device are you using?! Android will now crash this app...");
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "OOPS! Did not pick an EGLConfig. What device are you using?! Android will now crash this app...");
} else {
Log.w(TAG, "Using EGL CONFIG : ");
Apple2Activity.logMessage(Apple2Activity.LogType.WARN, TAG, "Using EGL CONFIG : ");
printConfig(egl, display, best);
}
return best;
@ -245,9 +245,9 @@ class Apple2View extends GLSurfaceView implements InputManagerCompat.InputDevice
private void printConfigs(EGL10 egl, EGLDisplay display, EGLConfig[] configs) {
int numConfigs = configs.length;
Log.w(TAG, String.format("%d configurations", numConfigs));
Apple2Activity.logMessage(Apple2Activity.LogType.WARN, TAG, String.format("%d configurations", numConfigs));
for (int i = 0; i < numConfigs; i++) {
Log.w(TAG, String.format("Configuration %d:\n", i));
Apple2Activity.logMessage(Apple2Activity.LogType.WARN, TAG, String.format("Configuration %d:\n", i));
printConfig(egl, display, configs[i]);
}
}
@ -328,9 +328,9 @@ class Apple2View extends GLSurfaceView implements InputManagerCompat.InputDevice
int attribute = attributes[i];
String name = names[i];
if (egl.eglGetConfigAttrib(display, config, attribute, value)) {
Log.w(TAG, String.format(" %s: %d\n", name, value[0]));
Apple2Activity.logMessage(Apple2Activity.LogType.WARN, TAG, String.format(" %s: %d\n", name, value[0]));
} else {
// Log.w(TAG, String.format(" %s: failed\n", name));
// Apple2Activity.logMessage(Apple2Activity.LogType.WARN, TAG, String.format(" %s: failed\n", name));
while (egl.eglGetError() != EGL10.EGL_SUCCESS) ;
}
}

View File

@ -627,3 +627,37 @@ jlong Java_org_deadc0de_apple2ix_Apple2JoystickCalibration_nativePollJoystick(JN
return cxy;
}
void Java_org_deadc0de_apple2ix_Apple2Activity_nativeLogMessage(JNIEnv *env, jclass cls, jstring jJsonString) {
#if TESTING
return NULL;
#endif
const char *jsonString = (*env)->GetStringUTFChars(env, jJsonString, NULL);
JSON_ref jsonData = NULL;
bool ret = json_createFromString(jsonString, &jsonData);
assert(ret > 0);
(*env)->ReleaseStringUTFChars(env, jJsonString, jsonString); jsonString = NULL;
long type = LOG_TYPE_INFO;
json_mapParseLongValue(jsonData, "type", &type, 10);
char *tag = NULL;
json_mapCopyStringValue(jsonData, "tag", &tag);
char *mesg = NULL;
json_mapCopyStringValue(jsonData, "mesg", &mesg);
log_taggedOutputString((log_type_t)type, tag, mesg);
if (tag) {
FREE(tag);
}
if (mesg) {
FREE(mesg);
}
json_destroy(&jsonData);
}

View File

@ -95,14 +95,25 @@ void log_init(void) {
}
void log_outputString(const char * const str) {
log_taggedOutputString(LOG_TYPE_INFO, NULL, str);
}
void log_taggedOutputString(log_type_t type, const char * const tag, const char * const str) {
if (UNLIKELY(!str)) {
return;
}
#if defined(NDEBUG)
// TODO : make logging level dynamic+configurable ...
if (type <= LOG_TYPE_DEBUG) {
return;
}
#endif
#if DO_STDERR_LOGGING
{
#if defined(__ANDROID__) && !defined(NDEBUG)
__android_log_print(ANDROID_LOG_ERROR, "apple2ix", "%s", str);
__android_log_print(type, tag ? tag : "apple2ix", "%s", str);
#else
fprintf(stderr, "%s\n", str);
#endif
@ -113,14 +124,31 @@ void log_outputString(const char * const str) {
return;
}
// calculate timestamp ...
char timestamp[32];
{
time_t secs = time(NULL);
struct tm timeinfo = { 0 };
struct tm *t = gmtime_r(&secs, &timeinfo);
assert(t != NULL);
size_t count = strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", &timeinfo);
assert(count == 19);
}
char *buf = NULL;
ASPRINTF(&buf, "%s %s %s", timestamp, tag ? tag : "-", str);
if (UNLIKELY(!buf)) {
return;
}
pthread_mutex_lock(&log_mutex);
size_t expected_bytescount = strlen(str);
size_t expected_bytescount = strlen(buf);
size_t bytescount = 0;
do {
ssize_t byteswritten = 0;
TEMP_FAILURE_RETRY(byteswritten = write(logFd, str+bytescount, expected_bytescount-bytescount));
TEMP_FAILURE_RETRY(byteswritten = write(logFd, buf+bytescount, expected_bytescount-bytescount));
if (UNLIKELY(byteswritten <= 0)) {
break; // OOPS !
}
@ -139,6 +167,8 @@ void log_outputString(const char * const str) {
}
} while (1);
FREE(buf);
if (UNLIKELY(bytescount != expected_bytescount)) {
// logging is b0rked, shut it down ...
_log_stopLogging();

View File

@ -16,6 +16,15 @@
extern "C" {
#endif
// NOTE: These match Android settings
typedef enum log_type_t {
LOG_TYPE_VERBOSE = 2,
LOG_TYPE_DEBUG,
LOG_TYPE_INFO,
LOG_TYPE_WARN,
LOG_TYPE_ERROR,
} log_type_t;
#if VIDEO_OPENGL
extern GLenum safeGLGetError(void);
#else
@ -29,6 +38,9 @@ void log_init(void);
// print a string to the log file. Terminating '\n' is added.
void log_outputString(const char * const str);
// print a tag + string to the log file. Terminating '\n' is added.
void log_taggedOutputString(log_type_t type, const char * const tag, const char * const str);
#define _MYFILE_ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
#define _SIMPLE_LOG(...) \