I want to enable/disable an Android phone's GSM connection. I need to disable/enable calls and SMS as required. How might I do this?
EDIT: This solution will also turn off wifi, bluetooth, etc...
If you want to turn off radio only, I think it's related to this issue: http://code.google.com/p/android/issues/detail?id=1065
I am really pessimistic about finding a good solution, but curious to see other answers.
See the blog article Android: Controlling Airplane Mode ,
// Toggle airplane mode.
Settings.System.putInt(
context.getContentResolver(),
Settings.System.AIRPLANE_MODE_ON, isEnabled ? 0 : 1);
// Post an intent to reload.
Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
intent.putExtra("state", !isEnabled);
sendBroadcast(intent);
where isEnabled is whether airplane mode is enabled or not.
Don't forget you need the WRITE_SETTINGS permission to do this, though.
/* Toggle airplane mode for 1 of the 4 allowed types
* type allowed values: cell, wifi, bluetooth, nfc
*/
private void changeRadioComponentEnabled(Context context, String type, boolean radio_component_enabled, boolean reset){
// now toggle airplane mode from on to off, or vice versa
Settings.System.putInt(context.getContentResolver(), Settings.System.AIRPLANE_MODE_ON, radio_component_enabled ? 0 : 1);
// now change system behavior so that only one component is turned off
// this also affects the future behavior of the system button for toggling air-plane mode.
// to reset it in order to maintain the system behavior, set reset to true, otherwise we lazily make sure mobile voice is always on
Settings.System.putString(context.getContentResolver(), Settings.System.AIRPLANE_MODE_RADIOS, type);
// post an intent to reload so the menu button switches to the new state we set
Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
intent.putExtra("state", radio_component_enabled ? false : true);
context.sendBroadcast(intent);
// revert to default system behavior or not
if (reset){ Settings.System.putString(context.getContentResolver(), Settings.System.AIRPLANE_MODE_RADIOS, "cell,bluetooth,wifi,nfc"); }
// if reset to default is not chosen, always enable mobile cell at least
// but only if NOT changing the mobile connection...
else if (type.indexOf("cell") == 0) { Settings.System.putString(context.getContentResolver(), Settings.System.AIRPLANE_MODE_RADIOS, "cell");}
}//end method
naturally this require the permission android.permission.WRITE_SETTINGS, and for bluetooth android.permission.BLUETOOTH_ADMIN. For NFC you might need android.permission.NFC.
EDITS: heavily modified since original, since I was actually using this in a different way in my own app
Related
After reading through a bunch of stale guides and stackoverflows, I was able to usb adb to install an apk as a system app in /system/priv-app that successfully toggles AirplaneMode in Android oreo:
// method in Activity, called via click listener on a Button
private void setMobileRadioEnabled_Option1(boolean enabled) {
android.content.Context context = this;
int value = enabled ? 0 : 1;
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
Settings.System.putInt(
context.getContentResolver(),
Settings.System.AIRPLANE_MODE_ON, value);
} else {
Settings.Global.putInt(
context.getContentResolver(),
Settings.Global.AIRPLANE_MODE_ON, value);
}
}
Permissions in AndroidManifest.xml:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
Standard release build in AndroidStudio with custom signing keys, installed via:
adb root
adb remount
adb push app-release.apk /system/priv-app
adb shell chmod 644 /system/priv-app/app-release.apk
adb reboot
On reboot, the app is installed and I can run it without issue.
I check in the notifications drawer / status bar what things are like to start with:
I then click my Button in the app, and check what happens:
As you can see, airplane mode seems to be successfully enabled based on the status of the airplane mode icon. But wifi and cellular data continue to be connected, and the status bar doesn't replace the text "Android" with "Airplane mode". In this state, if I hop over to chrome, I can clearly load websites I've never visited before. So airplane mode doesn't in fact seem to be actually on.
What am I doing wrong? I expect turning on airplane mode via System.putInt() to have the same effect as tapping the airplane mode tile in the status bar. No exceptions or useful error information spitting to logcat when I execute the code.
Checking this answer it seems that you need to send a broadcast to notify that you changed the airplane mode.
The broadcast should be:
Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
intent.putExtra("state", < current value of the airplane mode >);
sendBroadcast(intent);
Alternatively to jonathanrz's additional code above, which I believe is closer to canonical, I found that the following worked in place of the Settings.System.putInt() code and did not require sending the intent (or adding the permission(s) necessary to send it), at least on Oreo. I created it by merging a few answers and offhand comments from other posts, particularly an answer sketch hidden in a comment by "Navas pk" on Toggle airplane mode in Android:
private void setMobileRadioEnabled_Option2(boolean enabled) {
try {
final ConnectivityManager mConnectivityManager = (ConnectivityManager) getSystemService(android.content.Context.CONNECTIVITY_SERVICE);
final Class mClass = Class.forName(mConnectivityManager.getClass().getName());
final Method setAirplaneMode = mClass.getDeclaredMethod("setAirplaneMode", Boolean.TYPE);
setAirplaneMode.setAccessible(true);
setAirplaneMode.invoke(mConnectivityManager, !enabled);
} catch (Exception e) {
e.printStackTrace();
}
}
Two questions:
How does setting LocationRequest.setPriority(priority) work with the "Location mode" device setting?
If application calls LocationRequest.setPriority(PRIORITY_HIGH_ACCURACY) is called and device setting is set to "Battery saving", I assume the application won't be able to use GPS?
Another question is, with FusedLocationApi how can I check if the device setting is set to High Accuracy?
Yes, you are right that device settings has more preference than what you are asking for. Hence, if device settings is set to "Battery Saver", application won't be able to use GPS.
You don't need FusedLocationProvider to check for location setting in device. Use below code:-
int locationMode =
Settings.Secure.getInt(activityUnderTest.getContentResolver(),
Settings.Secure.LOCATION_MODE);
Check locationMode for possible return values.
if(locationMode == LOCATION_MODE_HIGH_ACCURACY) {
//request location updates
} else { //redirect user to settings page
startActivity(new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS));
}
DEFINITION:
My non-android device (NAD) is a Bluetooth device who loops its name from 60 to 0 and resets in an infinite fashion.
OBJECTIVE:
What I'm trying is to do is to have my android device as closely as possible detect that countdown and initiate an alarm as close to that of the NAD counter as possible.
I'm doing this by getting the native BluetoothAdapter of my device to startDiscovery() manually by tying the function to onscreen buttons and keeping an eye on the toasts I set through my BroadcastReceiver, which updates onscreen Textviews Which enables me to monitor what my device is receiving in real-time
REQUIREMENT:
System & resource efficiency is not a concern in this context.
PROBLEM:(Keep an eye out for PART 1 and PART 2 in the code)
I'm not sure how using fetchUuidsWithSdp() is helping me since the TextView it's updating remains empty and the Textview getting populated by the EXTRA_NAME extra from intent returning action ACTION_NAME_CHANGED is the cached, initial discovery name (ie. my application is not reading a name after initial discovery).
my code can be found below
Sorry for any newbie mistakes,I'm trying my best :)
public class BTBroadcastReceiver extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
//pulling the action name from the broadcasted intent
String action = intent.getAction();
if(BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)){
sToaster("StartedD");//show toast that Discovery has started
}
else if(BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)){
sToaster("EndedD");//show toast signifying end of discovery
/*
if(notfound){
mBTAdapter.startDiscovery();
}*/
}
else if(BluetoothDevice.ACTION_FOUND.equals(action)){
//when a device is found
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
//make sure it's indeed my NAD device by checking MAC address
if(device.getAddress().equals(MACAddress)){
if(notfound){
//show device name on screen
sToaster("FOUND DEvice");
notfound = false;
NAD = device;
NameShower.setText(device.getName());
}
else{
//do nothing if it's the second time the device is found
}
}
}
else if(BluetoothDevice.ACTION_NAME_CHANGED.equals(action)){
//name changed
BluetoothDevice foundDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
//make sure it's indeed my NAD device
if(foundDevice.equals(NAD)){
sToaster("Name Change!"); //show on screen that the name change intent has been caught
//PART1
//to prevent caching of the old device name StackOverflow article
//advised using this function i don't totally understand yet
//NAD.fetchUuidsWithSdp();
//either commented or not commented the outcome is the same (no refresh of the name)
//PART2
//tried showing the new name two different ways below, neither of which are effective
//by inspecting the TextViews on-screen
NameShower.setText(foundDevice.getName());
EventView.setText(intent.getStringExtra(BluetoothDevice.EXTRA_NAME));
}
}
}
};
I have worked on a bluetooth project and what I perceived was that the discovery process should be in an Intent which can be left registered in the background. And to discover the devices in range, you just need to invoke the BTDevice.startDiscovery() to search them.
Generally the startDiscovery() drains battery if enabled continuously.
If you want, I can edit this post to share a snipet that I used to scan for devices.
Hope this helps !
I've written the code below to set the phone into airplane mode to save power. The devices is being used as a WiFi-Hotspot to relay data from some sensors in a village in Indonesia. The sensors send their data at the same time so I just need to come out of airplane mode for five minutes at midnight and then reenter airplane mode.
The problem is the cellular radio is not shut off and the airplane icon does not appear. Though the the phone reports its status as airplane_mode on, it is still possible to call it. Other widgets in the marketplace seem to fare no better. I've tried "Airplane Mode Wi-Fi Tool". It too can not get the airplane icon to appear nor disable cell radio. When watching LogCat while using the device settings to go to Airplane mode, I can see that much more is happening than when trying from the program.
If I load my program on a Droid, this code works as expected. AIRPLANE_MODE_RADIOS is set to cell, bluetooth, wifi.
The offending device is a Samsung Galaxy 5, I5500 tested with:
-Froyo 2.2 build FROYO.UYJP2
-Froyo 2.2.1 build FROYO.UYJPE
One interesting side note: if I programmatically set airplane mode and then power cycle the device, it comes up in full airplane mode, rejects incoming calls etc.
Do others have similar stories with this or other devices? Is there a way to specifically turn off cell only?
public static void setAirplaneMode(Context context, boolean status) {
boolean isAM = Settings.System.getInt(context.getContentResolver(),
Settings.System.AIRPLANE_MODE_ON, 0) != 0;
String radios = Settings.System.getString(context.getContentResolver(),
Settings.System.AIRPLANE_MODE_RADIOS);
//This line is reporting all radios affected but annunciator does not seem to think so. Does not show airplane
Wake.logger("Airplane mode is: " + isAM + " changing to " + status + " For radios: " + radios, false);
// It appears Airplane mode should only be toggled. Don't reset to
// current state.
if (isAM && !status) {
Settings.System.putInt(context.getContentResolver(),
Settings.System.AIRPLANE_MODE_ON, 0);
Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
intent.putExtra("state", 0);
context.sendBroadcast(intent);
return;
}
if (!isAM && status) {
Settings.System.putInt(context.getContentResolver(),
Settings.System.AIRPLANE_MODE_ON, 1);
Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
intent.putExtra("state", 1);
context.sendBroadcast(intent);
return;
}
}
Classic bit twister error. The extra data argument in the broadcast intent needed to be true/false, not 1/0. Ugh!!!
intent.putExtra("state", true); //Not 1!!
One phone worked another didn't. Now both do.
My app allows the user to access their corporate voice mail. Normally, durring a phone call when the user holds the device up to their ear, the screen shuts off so they wont accidentally push buttons with their face. I would like to make my app do the same thing when the user is listening to their voice mail.
anyone know how to do this?
If you are allowed to look at open source code without causing yourself problems, check the source of the Android Phone Application. Specifically src/com/android/phone/PhoneApp.java and src/com/android/phone/InCallScreen.java.
From src/com/android/phone/PhoneApp.java:
//Around line 519
// Wake lock used to control proximity sensor behavior.
if ((pm.getSupportedWakeLockFlags()
& PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK) != 0x0) {
mProximityWakeLock = pm.newWakeLock(
PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK,
LOG_TAG);
}
....
// Around line 1334
if (((state == Phone.State.OFFHOOK) || mBeginningCall)&& !screenOnImmediately) {
// Phone is in use! Arrange for the screen to turn off
// automatically when the sensor detects a close object.
if (!mProximityWakeLock.isHeld()) {
if (DBG) Log.d(LOG_TAG, "updateProximitySensorMode: acquiring...");
mProximityWakeLock.acquire();
} else {
if (VDBG) Log.d(LOG_TAG, "updateProximitySensorMode: lock already held.");
}
} else {
// Phone is either idle, or ringing. We don't want any
// special proximity sensor behavior in either case.
if (mProximityWakeLock.isHeld()) {
if (DBG) Log.d(LOG_TAG, "updateProximitySensorMode: releasing...");
// Wait until user has moved the phone away from his head if we are
// releasing due to the phone call ending.
// Qtherwise, turn screen on immediately
int flags =
(screenOnImmediately ? 0 : PowerManager.WAIT_FOR_PROXIMITY_NEGATIVE);
mProximityWakeLock.release(flags);
}
}
Additionally, if you look at the code for the PowerManager class, PROXIMITY_SCREEN_OFF_WAKE_LOCK is documented (but hidden) and should do what you want ( I am not sure which API level this works for, however ) -- but not in the table for some reason.
/**
* Wake lock that turns the screen off when the proximity sensor activates.
* Since not all devices have proximity sensors, use
* {#link #getSupportedWakeLockFlags() getSupportedWakeLockFlags()} to determine if
* this wake lock mode is supported.
*
* {#hide}
*/
public static final int PROXIMITY_SCREEN_OFF_WAKE_LOCK = WAKE_BIT_PROXIMITY_SCREEN_OFF;
If you aren't afraid of using a potential undocumented feature, it should do exactly what you need.
as of API level 21 (Lollipop) you can get proximity wake lock this just like that:
if(powerManager.isWakeLockLevelSupported(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK)) {
PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, TAG);
wakeLock.setReferenceCounted(false);
return wakeLock;
} else {
return null;
}
}
then it is up to you to acquire and release the lock.
PS: PowerManager#getSupportedWakeLockFlags was hidden, but now exists nomore. They have invented isWakeLockLevelSupported instead.
Probably you don't need it anymore but for the ones that are interested in code you could have a look at my SpeakerProximity project at http://code.google.com/p/speakerproximity/
What you are seeing is the use of a proximity sensor. For devices that have one, you access it through SensorManager.