I have a piece of code that makes phone calls and hangs up after a certain amount of time.
I've managed to make calls from both SIMs (using different tricks for the 2nd SIM), however, Android does not seem to be able to detect whether the 2nd SIM is off-hook;
Take a look at this piece of code:
Class<?> c = Class.forName(telMgr.getClass().getName());
Method m = c.getDeclaredMethod("getITelephony");
m.setAccessible(true);
ITelephony telephonyService = (ITelephony)m.invoke(telMgr);
if (telephonyService.isOffhook()) { // DO SOMETHING }
If the first SIM makes the call, I get isOffHook() to be true, but from the second SIM, the phone is in progress, but I get false.
Is there a way to detect if I'm off-hook on both SIMs?
Thanks
Thanks for the comments, but I have found a solution.
Rather than use old methodology of retrieving the ITelephony "instance" from the TelephonyManager (I used this trick in older versions cause other ways were making me troubles), I use the TelephonyManager directly by calling getCallState(), and it seems informative and accurate for both SIMs.
A code sample:
TelephonyManager telMgr = (TelephonyManager)(this.getMainContext()
.getSystemService(Context.TELEPHONY_SERVICE));
/* Making a call... */
if (telMgr.getCallState() != TelephonyManager.CALL_STATE_OFFHOOK) { /* Do your stuff */ }
Simple and straight forward. Working with my current 5.1 Lollipop version.
Related
In my application, I need to make a call to a number, but the call must be made hands-free. I tried many available methods, min the method from this link.
The application logs showed the action of switching to hands-free, but physically it had no effect.
I also tried to do it with the help of such code:
private void enableHandsFree() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, ClassNotFoundException {
Class audioSystemClass = Class.forName("android.media.AudioSystem");
Method setForceUse = audioSystemClass.getMethod("setForceUse", int.class, int.class);
// First 1 == FOR_MEDIA, second 1 == FORCE_SPEAKER. To go back to the default
// behavior, use FORCE_NONE (0).
setForceUse.invoke(null, 1, 1);
}
Unfortunately also without success. Is there any way to handle such a problem?
Tested on Android 10
Undocumented API calls have been progressively disallowed in the last few android versions. The code you posted may work on on older APIs (my guess would be <= 26), but it'll have no effect in the newer versions.
For security reasons, no app is allowed to control audio output used by other apps. The only app that can do that is the one that as audio focus. And you're specifically trying to control audio output of the Phone app
I posted this on Android dev group. I'm hoping I can get some feedback here.
The PhoneStateListener's callbacks onCellLocationChanged and onSignalStrengthsChanged were the goto methods for when I wanted to handle cell and signal data changes in GSM and CDMA. With API 17+, I can see that there's a new callback (onCellInfoChanged) for handling both cell and signal changes.
Looking at the documentation, it's not clear what I can expect from the introduction of this new callback.
Will LTE changes always and only trigger onCellInfoChanged?
Will GSM/CDMA changed remain on the older callbacks?
Does one overlap with the other? (i.e. Both old and new get triggered for LTE or GSM/CDMA.)
It may very well be that different OEMs will have different implementations (sigh!), but I'm hoping there are guidelines that everyone's supposed to follow.
Can anyone shed some light on this?
Thanks,
Sebouh
I didn't test if but it looks from the code that both will be called.
I downloaded code source of Android 4.3(API 18) using the SDK Manager.
The following observations made me think that both would be called.
The class that triggers these events is: com.android.server.TelephonyRegistry
It notifies the listener though:
public void listen(String pkgForDebug, IPhoneStateListener callback, int events, boolean notifyNow)
This same function calls for both type of notifications(Location and CellInfo) in a non exclusive way.
On line 256:
if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION)) {
try {
if (DBG_LOC) Slog.d(TAG, "listen: mCellLocation=" + mCellLocation);
r.callback.onCellLocationChanged(new Bundle(mCellLocation));
} catch (RemoteException ex) {
remove(r.binder);
}
}
This one will call onCellLocationChanged even on new LTE phone since there is nothing from the above code that would prevent this. This needs double checking that there is no upper layer that filters the events themselves
On line 300 in the same code:
if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO)) {
try {
if (DBG_LOC) Slog.d(TAG, "listen: mCellInfo=" + mCellInfo);
r.callback.onCellInfoChanged(mCellInfo);
} catch (RemoteException ex) {
remove(r.binder);
}
}
There are other things from the code that look like CDMA will be calling the newer API. For example com.android.internal.telephony.cdma.CdmaLteServiceStateTracker seems to be dealing with CDMA and LTE. Again it would require a more careful look but that should give you a good place to start.
You can also try to simulate that with the emulator.
I am working on an android that will allow the user to add numbers to a blacklist. When an incoming number matches a number in the blacklist then the call should be rejected, even if the phone does ring briefly and then disconnects the call.
Everything I've found including on SO, says it can't be done without creating AIDL in com.android.internal.telephony which I've created but I can't add the modify phone state permission as it says it needs to be a system app.
I am targetting ICS upwards and I have seen other apps block calls in ICS and up so how is this done. I've also tried adding the modify phone state permission to the manifest file and it displays an error saying that it is only available for system apps so how do I get around this issue. I don't want the app to have to be rooted.
Thanks for any help you can provide
I've found the answer by a bit of luck.
Instead of adding permission MODIFY_PHONE_STATE add permission CALL_PHONE
Create a new package called com.android.internal.telephony
Inside this package create a file called ITelephony.aidl and add the following content
package com.android.internal.telephony;
interface ITelephony {
boolean endCall();
void answerRingingCall();
void silenceRinger();
}
Use the below code in order to block the call
try
{
TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
Class c = Class.forName(tm.getClass().getName());
Method m = c.getDeclaredMethod("getITelephony");
m.setAccessible(true);
com.android.internal.telephony.ITelephony telephonyService = (ITelephony)m.invoke(tm);
//telephonyService.silenceRinger();
telephonyService.endCall();
}
catch (Exception e)
{
Log.d("BLOCK CALL", e.toString());
Toast.makeText(context, e.toString(), Toast.LENGTH_LONG).show();
}
Hope this helps others, its not too easy to find. I don't understand why Google removed the ability to do this without mucking about like this though.
My Android app monitors cellular signal strengths. On CDMA devices, it works without any problems. On many GSM devices, it works without any problems. However, on some GSM devices, I am getting force closes with the following error message:
java.lang.ClassCastException: android.telephony.cdma.CdmaCellLocation cannot be cast to android.telephony.gsm.GsmCellLocation
I am fairly certain that this issue occurs on some Samsung devices in the US when they receive a 4G LTE signal; I believe it occurs on other devices and countries as well. I am still trying to determine the exact service providers and devices involved.
Here is the relevant snippet of code from MyService.java; I have marked the line referenced by the error message with //********:
public void onCreate() {
tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
...
}
public int onStartCommand(Intent in, int flags, int startId) {
signalStrengthListener = new SignalStrengthListener();
((TelephonyManager)getSystemService(TELEPHONY_SERVICE)).listen(signalStrengthListener, SignalStrengthListener.LISTEN_SIGNAL_STRENGTHS | SignalStrengthListener.LISTEN_SERVICE_STATE | SignalStrengthListener.LISTEN_DATA_CONNECTION_STATE | SignalStrengthListener.LISTEN_CELL_LOCATION);
return START_STICKY;
...
}
public void onSignalStrengthsChanged(SignalStrength signalStrength) {
isGSM = signalStrength.isGsm();
if (isGSM == true) {
GCellLoc = (GsmCellLocation)tm.getCellLocation(); //********
...
}
Not sure why is anything from android.telephony.cdma is appearing if this code should only be triggered when isGSM returns true. Perhaps when an LTE signal is detected, that puts the phone in some sort of dual CDMA/GSM mode as far as the Android API is concerned? I have not been able to find any documentation of similar behavior. How can I handle this in my code? Thanks.
I cannot explain why it is happening. I have some theories, but you have similar ones.
While you sort out the reason, you can deal with issue pragmatically. You know that sometimes even though you are getting a GSM SignalStrength in your callback, sometimes on some devices you get a CDMA CellLocation from TelephonyManager and write your code to handle that case using instanceOf instead of relying on .isGSM() from the SignalStrength.
CellLocation cellLoc = tm.getCellLocation();
if(cellLoc instanceof GsmCellLocation) {
GCellLoc = (GsmCellLocation) cellLoc;
// do work
}
Just a quick background I'm Running CM7 on a rooted Nexus one.
I am trying to detect when an outgoing call is actually connected: has stopped ringing and the person you are calling has answered. Looking through the forums this seems to be a tough and perhaps unanswered question. I'd really appreciate any insight into this.
In my searching the best I could find was in:
Android : How to get a state that the outgoing call has been answered?
#PattabiRaman said: "instead of detecting the outgoing call connection state, it is easy to get the duration of the last dialed call."
Does he mean that one should get the duration of the last dialed call as the call is in progress? And when that duration goes over 0 then you know?
The class com.android.internal.telephony.CallManager should have information about when the call actually is answered. It has a public static method getInstance() which returns the CallManager instance, and a public method getActiveFgCallState() which returns the current call state as a Call.State enum.
So in theory something like this might work:
Method getFgState = null;
Object cm = null;
try {
Class cmDesc = Class.forName("com.android.internal.telephony.CallManager");
Method getCM = cmDesc.getMethod("getInstance");
getFgState = cmDesc.getMethod("getActiveFgCallState");
cm = getCM.invoke(null);
} catch (Exception e) {
e.printStackTrace();
}
And then repeatedly poll the state:
Object state = getFgState.invoke(cm);
if (state.toString().equals("IDLE")) {
...
} else if (state.toString().equals("ACTIVE")) {
// If the previous state wasn't "ACTIVE" then the
// call has been established.
}
I haven't verified that this actually works. And even if it does you'll have to keep in mind that the API could change, since this isn't something that app developers are supposed to rely on.
I have looked into the code.
It will always give null unless you instantiate a Phone object and set it as default Phone.
But instantiating it needs some System permissions allowed only to system aps.
By using this method:
com.android.internal.telephony.PhoneFactory# public static void makeDefaultPhones(Context context) {
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.4_r1.2/com/android/internal/telephony/PhoneFactory.java