A week ago, for a week I tried to have an app I'm making completely independent detecting call states. I'm on Lollipop 5.1, so I can't use PRECISE_CALL_STATE that exists from Marshmallow upwards. I'm restricted to the usual CALL_STATE_RINGING, OFFHOOK and IDLE.
My extensive research (majority here on StackOverflow) with Google's help made me realize there's possibly no other way of detecting call states without these 3 on Lollipop or lower, or without system permissions on newer Android versions. There are very precise call states on all firmwares. But from a thread here on SO, it seems they can only be used by the current phone app (which I won't replace - will still be the normal Phone app). So no way to use those states in this case, it seems.
I'm also writing this question and answering it right away because I was able to make something which works just fine for me with the app I'm making. If it were a phone app, it would have to be more precise though. But in my case, I don't mind at all.
And as I see so many questions about parts of this question, I decided to put it all in the same question and answer it with something I tried to do and went well enough for me, and hopefully for some others. Feel free to suggest improvements and/or post other solutions!
If I did anything wrong on asking and answering right away please correct me. I never did this before.
With what I wrote on the question in mind, I was forced to use these 3 states, or use the call history. As I wanted the app to be as independent as possible, I tried to make the app detect the PRECISE_CALL_STATEs from itself. Except when there are too many calls. In case they're 3 or more, I must go get the call state of some from the call history (except one case if there are 3 calls, which is explained in the code). I just can't detect calls on hold, sadly.
So far I can detect the following cases:
Incoming call;
Incoming call waiting;
Outgoing call;
Call just lost;
Call lost some time ago;
Call just answered;
Call answered some time ago;
Call just finished;
Call finished some time ago.
And this is the code which I made to make this happen. By the way, as this has some lines already and I found out Stack Exchange network websites have an automatic license, then add this one to it (as I say in my profile), so anyone can do whatever they want with it, including copy-pasting (may or may not take time to understand, since when I did this first without comments, I had a hard time understanding what I had done):
This work marked with CC0 1.0 Universal. To view a copy of this
license, visit https://creativecommons.org/publicdomain/zero/1.0
Hope this is big enough to worry about license issues. Saw about this a week ago and I've began to think when to put license notice of CC0 on code I post. If it's not necessary here, please tell me so I can improve the "detection" next times.
public static final String CALL_PHASE_OUTGOING = "CALL_PHASE_OUTGOING";
public static final String CALL_PHASE_RINGING_NEW = "CALL_PHASE_RINGING_NEW";
public static final String CALL_PHASE_LOST = "CALL_PHASE_LOST";
public static final String CALL_PHASE_LOST_LATE = "CALL_PHASE_LOST_LATE";
public static final String CALL_PHASE_RINGING_WAITING = "CALL_PHASE_RINGING_WAITING";
//public static final String CALL_PHASE_ON_HOLD = "CALL_PHASE_ON_HOLD";
public static final String CALL_PHASE_ANSWERED = "CALL_PHASE_ANSWERED";
public static final String CALL_PHASE_ANSWERED_LATE = "CALL_PHASE_ANSWERED_LATE";
public static final String CALL_PHASE_FINISHED = "CALL_PHASE_FINISHED";
public static final String CALL_PHASE_FINISHED_LATE = "CALL_PHASE_FINISHED_LATE";
public static final String BETTER_CALL_STATE_OUTGOING = "BETTER_CALL_STATE_OUTGOING";
public static final String BETTER_CALL_STATE_INCOMING = "BETTER_CALL_STATE_INCOMING";
public static final String BETTER_CALL_STATE_WAITING = "BETTER_CALL_STATE_WAITING";
public static final String BETTER_CALL_STATE_DISCONNECTED = "BETTER_CALL_STATE_FINISHED";
//public static final String BETTER_CALL_STATE_ON_HOLD = "BETTER_CALL_STATE_ON_HOLD";
public static final String BETTER_CALL_STATE_ACTIVE = "BETTER_CALL_STATE_ACTIVE";
/**
* <p>This gets the phase of the call when a new phone state is detected (RINGING, OFFHOOK, or IDLE).</p>
* <p>There are values ending in "_LATE". Those are so because they're only detected after the end of all calls are over and after the phone gets to IDLE state.
* Which means, they already happened some time ago (1 second, 10 minutes, unpredictable).</p>
* <br>
* <p><b><u>---CONSTANTS---</u></b></p>
* <p>- <u>CALL_PHASE_OUTGOING [int]</u> --> returned in case an outgoing call was just started (whether it is answered or not by the other party - it's not possible to detect that easily).</p>
* <p>- <u>CALL_PHASE_RINGING_NEW [int]</u> --> returned in case it's a new incoming call.</p>
* <p>- <u>CALL_PHASE_LOST [int]</u> --> returned in case the call has just been lost.</p>
* <p>- <u>CALL_PHASE_LOST_LATE [int]</u> --> returned in case the call was lost some time ago already.</p>
* <p>- <u>CALL_PHASE_RINGING_WAITING [int]</u> --> returned in case there's a new call which is waiting to be answered (some call is already active).</p>
* <p>- <u>CALL_PHASE_ANSWERED [int]</u> --> returned in case the call has just been answered.</p>
* <p>- <u>CALL_PHASE_ANSWERED_LATE [int]</u> --> returned in case the call was answered some time ago alreaedy.</p>
* <p>- <u>CALL_PHASE_FINISHED [int]</u> --> returned in case the call was just finished (after having been answered - if it wasn't answered, it was LOST or LOST_LATE).</p>
* <p>- <u>CALL_PHASE_FINISHED_LATE [int]</u> --> returned in case the call was finished some time ago already (the same in parenthesis for FINISHED applies here).</p>
* <p><b><u>---CONSTANTS---</u></b></p>
*
* #param context <u>[Context]</u> --> Context of the application.
* #param state <u>[int]</u> --> One of the CALL_STATE in TelephonyManager.
* #param incomingNumber <u>[String]</u> --> Phone number that came with the state change.
* #param calls_state <u>[ArrayList(ArrayList(String))]</u> --> An ArrayList of the indicated type that will have the list of calls currently in processing
* (put empty in the beginning and keep the object where the array was created in memory, so the contents of the array are kept, or save it somewhere,
* but don't give always an empty one - this method handles cleaning it when needed. Just give it empty in the beginning of the app and let the method
* handle it from there).
* #param map_CallLog_to_CALL_PHASE <u>[LinkedHashMap(Integer, String)]</u> --> A map with the TYPEs in CallLog.Call on its keys, and on its valus, the corresponding CALL_PHASEs.
* Example: <br><br>map_CallLog_to_CALL_PHASE.put(CallLog.Calls.INCOMING_TYPE, CALL_PHASE_ANSWERED);
* <br>map_CallLog_to_CALL_PHASE.put(CallLog.Calls.MISSED_TYPE, CALL_PHASE_LOST).
*
* #return <u>[ String[][] ]</u> --> A double array of Strings in which each element contains the number and the phase call (CALL_PHASE) in which the number is currently in.
* There can be more than one event in a state change. It may be understood that a call had already been finished some time ago, or lost some time ago.
* Though, the events will always be in the actual event order. If a call was lost before another was answered, then the order will be exactly that one and not the opposite.
*/
public static String[][] get_call_phase(Context context, int state, String incomingNumber, ArrayList<ArrayList<String>> calls_state, LinkedHashMap<Integer, String> map_CallLog_to_CALL_PHASE) {
switch (state) {
case (CALL_STATE_RINGING): {
//System.out.println("RINGING - " + incomingNumber);
// New incoming call (there are no calls in the current processing call list).
if (calls_state.size() == 0) { // Which means, was in IDLE.
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add(incomingNumber);
arrayList.add(BETTER_CALL_STATE_INCOMING);
calls_state.add(arrayList);
System.out.println(CALL_PHASE_RINGING_NEW + " -> " + incomingNumber);
return new String[][]{new String[]{incomingNumber, CALL_PHASE_RINGING_NEW}};
} else {
// New incoming call waiting
for (int i = 0; i < calls_state.size(); i++) {
if (calls_state.get(i).get(1).equals(BETTER_CALL_STATE_ACTIVE)) {
// If any call was already active and another one came, then that other one is waiting to be answered.
// This also works with 3 calls, even on case 8, since the state of the 1st call only changes on IDLE.
// Until then it remains ACTIVE, even having been already disconnected (don't know a way to detect it was disconnected).
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add(incomingNumber);
arrayList.add(BETTER_CALL_STATE_WAITING);
calls_state.add(arrayList);
System.out.println(CALL_PHASE_RINGING_WAITING + " -> " + incomingNumber);
return new String[][]{new String[]{incomingNumber, CALL_PHASE_RINGING_WAITING}};
//break;
}
}
}
break;
}
case (CALL_STATE_OFFHOOK): {
//System.out.println("OFFHOOK - " + incomingNumber);
String[] to_return = null;
/*if (calls_state.size() == 0) {
// If there are no calls in processing (for example, the app was started with at least one call already in course), abort and do nothing at all.
// Can't have this here... Or it won't detect an outgoing call, which puts the phone in this state in the beginning.
break;
}*/
// Check if it's an outgoing call.
if (calls_state.size() == 0) { // Ou seja, estava em IDLE.
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add(incomingNumber);
arrayList.add(BETTER_CALL_STATE_OUTGOING);
calls_state.add(arrayList);
System.out.println(CALL_PHASE_OUTGOING + " -> " + incomingNumber);
to_return = new String[]{incomingNumber, CALL_PHASE_OUTGOING};
} else {
// Check if the 1st or only call was answered.
for (int i = 0; i < calls_state.size(); i++) {
if (PhoneNumberUtils.compareStrictly(calls_state.get(i).get(0), incomingNumber)) {
if (calls_state.get(i).get(1).equals(BETTER_CALL_STATE_INCOMING)) {
// If the number was in INCOMING (not WAITING, because I don't know how to detect a call waiting that is answered)
// and we are now in the OFFHOOK state, then the call was answered.
calls_state.get(i).set(1, BETTER_CALL_STATE_ACTIVE);
System.out.println(CALL_PHASE_ANSWERED + " -> " + incomingNumber);
return new String[][]{new String[]{incomingNumber, CALL_PHASE_ANSWERED}};
}
}
}
}
// Add the number to the list with the state OFFHOOK, or update the state in case the number is already on the list.
// This is for in case the cases above don't apply --> WAITING to OFFHOOK (don't know what to do with that - can't be rejected or answered).
// Then in that case, I leave the state CALL_STATE_OFFHOOK on the list.
for (int i = 0; i < calls_state.size(); i++) {
if (PhoneNumberUtils.compareStrictly(calls_state.get(i).get(0), incomingNumber)) {
calls_state.get(i).set(1, String.valueOf(CALL_STATE_OFFHOOK));
break;
}
}
return new String[][]{to_return};
//break;
}
case (CALL_STATE_IDLE): {
//System.out.println("IDLE - " + incomingNumber);
ArrayList<String[]> final_return = new ArrayList<>();
if (calls_state.size() == 0) {
// If there are no calls in processing (for example, the app was started with at least one call already in course), abort and do nothing at all.
break;
}
System.out.println("Aqui:");
for (int i = 0; i < calls_state.size(); i++) {
System.out.println(calls_state.get(i).get(0) + " | " + calls_state.get(i).get(1));
}
//////////////////////////////////////
// Beginning of the LATE events
// We begin by the LATE events for the correct order to go inthe return array of all events, the closes to reality possible.
// Bellow is the handling of all numbers that didn't came with the state IDLE. Only one can come with the state and is the one for which the call was finished right now or lost right now.
// The other would get no treatment. Therefore, this tried to understand what may have happened. All here will be of the LATE type because of exactly that (what happened --> past).
// If it's more than a call, we can apply a "trick" to know the state of the first and of the last - answered or lost. This is an example of the cases 1 and 6 (2 calls) and 7 to 10 (3 calls).
if (calls_state.size() > 1) {
// If the first call would have been lost, this would have gone to IDLE directly, and the list would have gotten empty. Then the call coming next would
// be the first call again. If there is a 2nd, the 1st must have been answered. And if it was answered, it was finished in some moment.
// In case the 1st call wasn't the one that came in IDLE, then it was finished some time ago already.
if (!calls_state.get(0).get(0).equals(incomingNumber)) {
// In 2 calls, if the 2nd comes on IDLE, then it means the 1st one was already finished a some time ago (because, again, if the 1st wasn't answered,
// there would be no 2nd). And this sets that state in the call and returns it.
// This can be applied for 3 or more calls too. In that case, if any call not the 1st gets on IDLE, the 1st was already finished some time ago.
System.out.println(CALL_PHASE_FINISHED_LATE + " -> " + calls_state.get(0).get(0));
final_return.add(new String[]{calls_state.get(0).get(0), CALL_PHASE_FINISHED_LATE});
calls_state.get(0).set(1, BETTER_CALL_STATE_DISCONNECTED);
}
}
for (int i = 0; i < calls_state.size(); i++) {
if (PhoneNumberUtils.compareStrictly(calls_state.get(i).get(0), incomingNumber)) {
if (!(calls_state.get(i).get(1).equals(BETTER_CALL_STATE_INCOMING) || calls_state.get(i).get(1).equals(BETTER_CALL_STATE_WAITING))) {
// In case the call didn't come from INCOMING or WAITING, then check if it was answered some time ago or not.
// Which means, if it wasn't detected the call was answered in the right moment (so it's not in ACTIVE state)...
if (!calls_state.get(i).get(1).equals(BETTER_CALL_STATE_ACTIVE)) {
System.out.println(CALL_PHASE_ANSWERED_LATE + " -> " + calls_state.get(i).get(0));
final_return.add(new String[]{calls_state.get(i).get(0), CALL_PHASE_ANSWERED_LATE}); // ... then it was answered some time ago already.
}
}
break;
}
}
// For 3 or more calls, for all calls in the middle of the 1st and last, it's not possible to know their state without, at least, a way of knowing if any
// ended in the middle or not. There, it would be possible to know that the 2nd one was answered, for example (in the case of 3 calls). But without that,
// there's no way of knowing.
// So, in that case, for the remaining calls, we're forced to go to the phone's call history.
// This unless the case 9 happens. In that case, we can know the state of the 3 calls.
// Sum up: this is done for all calls, except the 1st and last, and the one that got to IDLE. Supposing it's the 2nd in the case of 3 calls, nothing it's done.
// In other cases, the calls that get here, we got get the state from the call history.
// And this is done here to go in the correct order in the return array. After the handling of the 1st call and before the handling of the last call. And on the LATE events.
if (calls_state.size() >= 3) {
for (int i = 1; i < calls_state.size()-1; i++) {
if (calls_state.get(i).get(0).equals(incomingNumber)) {
continue;
}
int tipo_chamada = obter_tipo_ultima_chamada_numero(context, calls_state.get(i).get(0));
if (tipo_chamada == CallLog.Calls.INCOMING_TYPE || tipo_chamada == CallLog.Calls.MISSED_TYPE) {
System.out.println(map_CallLog_to_CALL_PHASE.get(tipo_chamada) + " -> " + calls_state.get(calls_state.size() - 1).get(0));
final_return.add(new String[]{calls_state.get(calls_state.size() - 1).get(0), map_CallLog_to_CALL_PHASE.get(tipo_chamada)});
}
calls_state.get(calls_state.size() - 1).set(1, BETTER_CALL_STATE_DISCONNECTED);
}
// TODO Imagine the same person calls, gives up, calls again, I hang up who I was talking with and answered this person.
// This will detect the state as the last one (answered), when I didn't answered the 1st time.
// This should be in real-time detecting the exact states from the call history if it can't get them directly, but I don't have time to think on that.
// In the app I'm making (an assistant), I don't need all the states when the happen exactly. Only missed calls in the end and inoming calls in the beginning.
}
if (calls_state.size() > 1 && !calls_state.get(calls_state.size() - 1).get(0).equals(incomingNumber)) {
// The detection of the last call, in case it was lost some time ago, works both for 2 as for any other superior number of calls.
// For only one call it's not necessary, because on that case, we know exactly when it's lost.
// PS: The call that got to IDLE is never LOST_LATE - either lost right now or finished right now.
// If the last call on the list got the OFFHOOK state (which means, without knowing if it was answered or lost in the right moment),
// and didn't get here on IDLE, then by the cases 1 and 6 for 2 calls, and by the cases 7 to 10 for 3 calls, it wasn't answered either,
// or it would have been that call getting to IDLE itself.
// Getting the 1st one here on IDLE in the case of 2 calls or any other call in the case of 3 calls, then this last one was lost some time ago.
if (calls_state.get(calls_state.size() - 1).get(1).equals(String.valueOf(CALL_STATE_OFFHOOK))) {
System.out.println(CALL_PHASE_LOST_LATE + " -> " + calls_state.get(calls_state.size() - 1).get(0));
final_return.add(new String[]{calls_state.get(calls_state.size() - 1).get(0), CALL_PHASE_LOST_LATE});
calls_state.get(calls_state.size() - 1).set(1, BETTER_CALL_STATE_DISCONNECTED);
}
}
// End of LATE events
//////////////////////////////////////
// Now processing of the immediate events, so they get all in order (the late ones happened before the immediate ones).
for (int i = 0; i < calls_state.size(); i++) {
if (PhoneNumberUtils.compareStrictly(calls_state.get(i).get(0), incomingNumber)) {
if (calls_state.get(i).get(1).equals(BETTER_CALL_STATE_INCOMING) || calls_state.get(i).get(1).equals(BETTER_CALL_STATE_WAITING)) {
// If it came directly from INCOMING or WAITING states to IDLE, then the call was lost right now.
System.out.println(CALL_PHASE_LOST + " -> " + incomingNumber);
final_return.add(new String[]{incomingNumber, CALL_PHASE_LOST});
} else {
// If the state is not INCOMING or WAITING, this in case will be OFFHOOK or ANSWERED. Which means, the call was finished right now (means was alreaedy answered some ago - or would have been lost).
// In no case where a call goes from OFFHOOK to IDLE means the call was lost some time ago, from the testing. So the only option is the call having been finished, or lost, right now (which is handled on the above IF).
System.out.println(CALL_PHASE_FINISHED + " -> " + incomingNumber);
final_return.add(new String[]{incomingNumber, CALL_PHASE_FINISHED});
}
calls_state.get(i).set(1, BETTER_CALL_STATE_DISCONNECTED);
break;
}
}
calls_state.clear();
return final_return.toArray(new String[0][0]);
//break;
}
}
return null;
}
I call this from inside TelephonyManager.listen() method, when the phone state changes. That method I have in a constructor of a class which is instanciated on a service that never stops and it's the main service of the entire app. Basically, this object is always on memory. Read method description there to know what to do with it. Hopefully I explained decently. If I didn't, please tell me what I can explain better.
If anyone has a better solution, please feel free to share! I'd appreciate it! A better way than this one that I see is on every phone state change, go see if there was a change in the call history, depending on which change it was. But that would take even more time from me and I don't have enough for that. A week for this was already too much, since I had to borrow my mother, father and brother's phones, and sometimes the house phone to get to this hahaha. And they're not home all the time.
I'll leave a GitHub link here for the rest of what is needed to understand the code completely. I talk there about case number X and Y, for example. That's here: https://github.com/DADi590/Detect-better-call-states-on-Android, along with ideas that I had and didn't work or that I had but will not implement for lack of time.
In Java/Android, is there a way to check whether the currently executing line of code is executing on a background thread or not?
I have a lil' program I'm conjuring up that has finally reached the full-spaghetti stage... this was intentional, you see, because this way... if a competitor gets their hands on the code, and they "open the hood," after looking at it for more than 20 seconds, their hair will catch on fire and they'll run away screaming... but now even I am getting confused and I need to check for this condition somehow.
Exhibit-A:
// can be called from 1,067 places... some of which are background threads.
public void startDoingAFunDance(String caller, int wobbleIntensity, int spineAngle, int feetSeparationInInches) {
if (!validCallersForFunDance.contains(caller)) {
Log.i("XXX", "Caller not allowed.");
return;
}
boolean wasCalledFromBackgroundThread = // ? < what to put here > ?
Log.i("XXX", "Was startDoingAFunDance() called from a background thread? And the answer is: " + wasCalledFromBackgroundThread);
// classified
}
An easy way to know it might be the following
boolean wasCalledFromBackgroundThread = (Thread.currentThread().getId() != 1);
background threads doesn't have id 1 (UI thread has).
I'm working on an Android app that runs on remotely-sited, unattended devices. It has a Service to periodically retrieve settings from a fixed URL. It does this by daisy-chaining posts of a Runnable to a Handler obtained from a HandlerThread.getLooper(): it does an initial post() after creating the Handler in the Service's startup, and thereafter it does a postDelayed() at the end of each run through the Runnable.
This works "forever" on some devices in the project (fetching the settings successfully every 30 seconds for thousands of hours), but on other devices it stops working after a variable amount of time (hours to tens of hours) and thereafter never works again. I've even added code to monitor the Service's activity and restart the process by issuing a new post() if it's been more than 5 minutes since the last run, and that code kicks in when as intended when the process stalls, but still the Runnable no longer runs.
Some pared-down code snippets, and then further discussion of the problem/symptoms:
public class SettingsMonitor extends Service {
private HandlerThread mHandlerThread = new HandlerThread("SettingsMonitorHandler");
private Handler mSettingsMonitorHandler;
private long mLastTryMillis = 0;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
mHandlerThread.start();
mSettingsMonitorHandler = new Handler(mHandlerThread.getLooper());
mSettingsMonitorHandler.post(checkSettings);
return START_STICKY;
}
final Runnable checkSettings = new Runnable()
{
#Override
public void run() {
Log.d(TAG, "checking latest settings");
mLastTryMillis = System.currentTimeMillis();
getSettingsFromUrl(SETTINGS_URL);
// queue up next check
mSettingsMonitorHandler.postDelayed(checkSettings, appSettings.getSettingsMonitorIntervalMillis());
}
};
public void restartCheckingIfStalled() {
long settingsMonitorIntervalMillis = appSettings.getSettingsMonitorIntervalMillis();
long maxStalledMillis = 10 * settingsMonitorIntervalMillis;
long millisSinceLastCheck = System.currentTimeMillis() - mLastTryMillis;
if (millisSinceLastCheck > maxStalledMillis) {
mSettingsMonitorHandler.removeCallbacksAndMessages(null);
Log.d(TAG, "checkSettings stalled -- restarting...");
mSettingsMonitorHandler.post(checkSettings);
}
}
}
The app logs extensive debug info, including every exception caught in local catch blocks (e.g., inside getSettingsFromUrl()) as well as any exception caught in a global app-level UncaughtExceptionHandler -- there are no exceptions being thrown by the app. There are log entries made at many points in getSettingsFromUrl(), as well as the first line of the Runnable, but at some point all of these log entries cease to appear, so I infer that the Runnable itself is no longer actually running.
I monitor the Service from elsewhere in the app and so I know that it is running throughout the lifetime of the app (even after the Runnable no longer runs); at the same time, I call restartCheckingIfStalled() and I do see the log entries indicating that the re-start is being requested, but apparently the post() it issues does not cause the Runnable to run again.
It's a bit difficult to quantify, but it appears that this problem is occurring on devices that are showing other signs of having network connection problems (some are on wi-fi, some are on sim cards). I don't really know what to make of that possible correlation, though.
I am mystified that this process goes off the rails without any sign of trouble -- no exception is thrown, and the last successful run through the Runnable and getSettingsFromUrl() look completely normal. I am looking for suggestions about ways to isolate, identify, and ultimately fix the problem, because I've exhausted a bunch of ideas/tests and I'm still no closer to understanding it.
#x-code: Ok, here's getSettingsFromUrl() -- I don't (explicitly) set a timeout (I'll go search now for whether there is any kind of default). How might lack of timeout relate to or explain the symptoms I'm describing?
private boolean getSettingsFromUrl(String settingsUrl) {
Log.d(TAG, "trying settings file: " + settingsUrl);
try {
URL url = new URL(settingsUrl);
Scanner scanner = new Scanner(url.openStream());
while (scanner.hasNextLine()) {
String s = scanner.nextLine().split(SETTINGS_FILE_COMMENT_CHAR)[0].trim(); // remove any comments and trim leading/trailing whitespace
if (s.length() > 0) { // skip blank lines
AdminRequest req = new AdminRequest(s);
if (req.isSetReq()) { // only apply SET requests, not GET (or other nonsense)
String reqType = req.getReqType();
if (settingsToApply.containsKey(reqType)) { // override earlier settings with later
settingsToApply.remove(reqType);
}
settingsToApply.put(reqType, req);
}
}
}
scanner.close();
return true;
}
catch (FileNotFoundException e) {
Log.d(TAG, "settings file not found at " + settingsUrl);
}
catch (Exception e) {
Log.d(TAG, "error checking settings: " + Log.getStackTraceString(e));
}
return false;
}
This may beg further questions (about whether/how I use this function's return value -- which I do; about what these AdminRequests are and what I'm doing with them in the settingsToApply HashMap, etc). I'm willing to continue providing more info if it seems likely to lead to insight, but it's not clear to me why, at a minimum, I don't see the first "checking latest settings" message at the top of checkSettings()... how would a problem (e.g., lack of a timeout in accessing the URL) affect subsequent calls to checkSettings()? Do all invocations of checkSettings() run on the same thread, and it's tied up waiting "forever" to access the URL? If that's the case, why is restartCheckingIfStalled() not also blocked? I do get the Log.d() messages from that.
Edited to show the changes I made to add timeouts, which solved the problem per the responses I received: in getSettingsFromUrl() change these two lines
URL url = new URL(settingsUrl);
Scanner scanner = new Scanner(url.openStream());
to these:
URL url = new URL(settingsUrl);
URLConnection cxn = url.openConnection();
cxn.setConnectTimeout(URL_CONNECT_TIMEOUT_MILLIS);
cxn.setReadTimeout(URL_READ_TIMEOUT_MILLIS);
Scanner scanner = new Scanner(cxn.getInputStream());
Do all invocations of checkSettings() run on the same thread
Yes.
If that's the case, why is restartCheckingIfStalled() not also blocked?
Because it runs on your app's main (ui) thread.
The advantage of running a HandlerThread in a Service is that each message or Runnable that you post to it is placed in a queue, so you don't have to write thread-safe code.
One disadvantage is that this kind of blocking can occur. As GreyBeardedGeek points out, you should set a timeout other than 0 on your URL connection.
I suspect that the default connectionTimeout is 0 (wait forever).
And if the connection blocks 'forever', your handler thread will block as well - your subsequent posts will have no effect.
You should probably set both the connection timeout and the read timeout.
My result
Expected result
public void run () {
try {
handlers.addElement (this);
broadcast("Welcome " + name);
while(handlers.size() != 2){
if(handlers.size() > 2){
this.out.writeUTF ("The Room is full!");
this.out.flush();
handlers.removeElement(this);
socket.close();
}
}
broadcast("No of Player: " + handlers.size());
for(int i = 0; i < handlers.size(); i++){
GameHandler player = (GameHandler) handlers.get(i);
broadcast("Player " + (i + 1) + ": " + player.name);
}
System.out.println("Game starts!");
startGame(4);
....
}
protected static void broadcast (String message) {
synchronized (handlers) {
Enumeration e = handlers.elements ();
while (e.hasMoreElements ()) {
GameHandler handler = (GameHandler) e.nextElement ();
try {
handler.out.writeUTF (message);
handler.out.flush ();
} catch (IOException ex) {
handler.stop ();
}
}
}
}
The problem is the difference between the expected result and my actual result. I have no idea why the broadcast before the while loop runs normally but others run twice
Your problem is that in your case, each of the thread is sending the broadcast. Either you need to have a "master" / "server" of games thread that does the "system announcements" broadcasting, -or- elect one of the client threads (maybe the "player 1" thread?) to send the announcements.
The problem is the difference between the expected result and my actual result. I have no idea why the broadcast before the while loop runs normally but others run twice
You really don't give enough details on your problem but I see these issues:
You talk about TCP and the code mentions sockets but you are processing a local elements collection. Unless you are talking to the same JVM over TCP (which is strange) the elements collection is going to start 2 players on each client. Is that really what you expect?
Even though you says elements is a Vector you still need to synchronize on it at the start of the run() method because you are performing multiple operations on it and there are race conditions. For example, if 3 handlers are added, they will all remove themselves and close their own sockets.
Vector really is an outdated collection. You should be using something else.
When the first thread adds itself to elements it then enters a spin loop waiting for the second person to join the game. Seems like a waste there. Some small Thread.sleep(...) would be appropriate.
If the room is full I suspect that the thread should return; from the run() method. Instead it continues on which I suspect is not good.
Hope something here helps.
I want to check an huge amount (thousands) of Websites, if they are still running. Because I want to get rid of unececarry entries in my HostFile Wikipage about Hostfiles.
I want to do it in a 2 Stage process.
Check if something is running on Port 80
Check the HTTP response code (if it's not 200 I have to check the site)
I want to multithread, because if I want to check thousands of addresses, I cant wait for timeouts.
This question is just about Step one.
I have the problem, that ~1/4 of my connect attempts don't work. If I retry the not working ones about ~3/4 work? Do I not close the Sockets correctly? Do I run into a limit of open Sockets?
Default I run 16 threads, but I have the same problems with 8 or 4.
Is there something I'm missing
I have simplified the code a little.
Here is the code of the Thread
public class SocketThread extends Thread{
int tn;
int n;
String[] s;
private ArrayList<String> good;
private ArrayList<String> bad;
public SocketThread(int tn, int n, String[] s) {
this.tn = tn;
this.n = n;
this.s = s;
good = new ArrayList<String>();
bad = new ArrayList<String>();
}
#Override
public void run() {
int answer;
for (int i = tn * (s.length / n); i < ((tn + 1) * (s.length / n)) - 1; i++) {
answer = checkPort80(s[i]);
if (answer == 1) {
good.add(s[i]);
} else {
bad.add(s[i]);
}
System.out.println(s[i] + " | " + answer);
}
}
}
And here is the checkPort80 Method
public static int checkPort80(String host)
Socket socket = null;
int reachable = -1;
try {
//One way of doing it
//socket = new Socket(host, 80);
//socket.close();
//Another way I've tried
socket = new Socket();
InetSocketAddress ina = new InetSocketAddress(host, 80);
socket.connect(ina, 30000);
socket.close();
return reachable = 1;
} catch (Exception e) {
} finally {
if (socket != null) {
if (socket.isBound()) {
try {
socket.close();
return reachable;
} catch (Exception e) {
e.getMessage();
return reachable;
}
}
}
}
}
About Threads, I make a ArrayList of Threads, create them and .start() them and right afterwards I .join() them, get the "God" and the "Bad" save them to files.
Help is appreciated.
PS: I rename the Hosts-file first so that it doesn't affect the process, so this is not an issue.
Edit:
Thanks to Marcelo Hernández Rishr I discovered, that HttpURLConnection seems to be the better solution. It works faster and I can also get the HttpResponseCode, which I was also interested anyways (just thought it would be much slower, then just checking Port 80). I still after a while suddenly get Errors, I guess this has to do with the DNS server thinking this is a DOS-Attack ^^ (but I should examine futher if the error lies somewhere else) also fyi I use OpenDNS, so maybe they just don't like me ^^.
x4u suggested adding a sleep() to the Threads, which seems to make things a little better, but will it help me raise entries/second i don't know.
Still, I can't (by far) get to the speed I wanted (10+ entries/second), even 6 entries per second doesn't seem to work.
Here are a few scenarios I tested (until now all without any sleep()).
number of time i get first round how many entries where entries/second
threads of errors processed until then
10 1 minute 17 seconds ~770 entries 10
8 3 minute 55 seconds ~2000 entries 8,51
6 6 minute 30 seconds ~2270 entries 5,82
I will try to find a sweet spot with Threads and sleep (or maybe simply pause all for one minute if I get many errors).
Problem is, there are Hostfiles with one million entries, which at one entry per second would take 11 Days, which I guess all understand, is not expectable.
Are there ways to switch DNS-Servers on the fly?
Any other suggestions?
Should I post the new questions as separate questions?
Thanks for the help until now.
I'll post new results in about a week.
I have 3 suggestions that may help you in your task.
Maybe you can use the class HttpURLConnection
Use a maximum of 10 threads because you are still limited by cpu, bandwidth, etc.
The lists good and bad shouldn't be part of your thread class, maybe they can be static members of the class were you have your main method and do static synchronized methods to add members to both lists from any thread.
Sockets usually try to shut down gracefully and wait for a response from the destination port. While they are waiting they are still blocking resources which can make successive connection attempts fail if they were executed while there have still been too many open sockets.
To avoid this you can turn off the lingering before you connect the socket:
socket.setSoLinger(false, 0);