I need some help and hope to find an advice by asking you, clever people. I want to switch to airplane mode on Android JellyBean/Kitkat (API 17+)...
I searched for hours but I don't get a working result/information.
At the moment I could only switch WLAN and mobile data off - but cell connectivity will only be disabled after switching to airplane mode, right?
This works:
public void manual_switch_off()
{
WifiManager wifiManager;
wifiManager = (WifiManager) this.getSystemService(this.WIFI_SERVICE);
wifiManager.setWifiEnabled(false);
ConnectivityManager dataManager;
dataManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
Method dataMtd = null;
try
{
dataMtd = ConnectivityManager.class.getDeclaredMethod("setMobileDataEnabled", boolean.class);
} catch (NoSuchMethodException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
dataMtd.setAccessible(true);
try
{
dataMtd.invoke(dataManager, false);
} catch (IllegalArgumentException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
This second version (airplane mode) works not:
#SuppressWarnings("deprecation")
public void airPlanemodeON()
{
boolean isEnabled = Settings.System.getInt(this.getContentResolver(), Settings.System.AIRPLANE_MODE_ON, 0) == 1;
if (isEnabled == false)
{
modifyAirplanemode(true);
Toast.makeText(getApplicationContext(), "Airplane Mode ON", Toast.LENGTH_LONG).show();
}
}
#SuppressWarnings("deprecation")
public void airPlanemodeOFF()
{
boolean isEnabled = Settings.System.getInt(this.getContentResolver(), Settings.System.AIRPLANE_MODE_ON, 0) == 1;
if (isEnabled == true)// means this is the request to turn ON AIRPLANE mode
{
modifyAirplanemode(false);
Toast.makeText(getApplicationContext(), "Airplane Mode OFF", Toast.LENGTH_LONG).show();
}
}
#SuppressWarnings("deprecation")
public void modifyAirplanemode(boolean mode)
{
Settings.System.putInt(getContentResolver(), Settings.System.AIRPLANE_MODE_ON, mode ? 1 : 0);// Turning ON/OFF Airplane mode.
Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);// creating intent and Specifying action for AIRPLANE mode.
intent.putExtra("state", !mode);// indicate the "state" of airplane mode is changed to ON/OFF
sendBroadcast(intent);// Broadcasting and Intent
}
I set permissions to manifest.
Some people said I could use Titanium Backup to switch this app to an system-app, will this really work? - I want to be able to load my application to the play store - I could imagine that after making my app an system app it wouldn't be possible any more, is this correct?
Thanks a lot!
Currently you can not achive turning airplane mode with higher than jellybean because google ristrict that as protected broadcast
Related
I want to control wifi hotspot dynamically in my Android app project. I have tired Reflection (which will not work in Android Oreo and later versions), startLocalOnyNetwork (but I want specific SSID and PASSWORD, which is not possible to configure it).
Then I rooted my phone, Is it possible if the device is rooted ?
Expecting an api to turn on/off wifi hotspot with specific SSID and PASSWORD or use the previous one.
Any possibilities or workarounds ?
Thanks in advance.
To turn on Wifi Hotspot, need some permissions
<uses-permission android:name="android.permission.WRITE_SETTINGS"
tools:ignore="ProtectedPermissions" />
and the permission should be dynamically granted by user
In apps advanced settings -> Modify system settings
/**
* This enables tethering using the ssid/password defined in Settings App>Hotspot & tethering
* Does not require app to have system/privileged access
* Credit: Vishal Sharma - https://stackoverflow.com/a/52219887
*/
public boolean startTethering() {
File outputDir = mContext.getCodeCacheDir();
Object proxy;
try {
proxy = ProxyBuilder.forClass(OnStartTetheringCallbackClass())
.dexCache(outputDir).handler(new InvocationHandler() {
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
}).build();
} catch (Exception e) {
Log.e(TAG, "Error in enableTethering ProxyBuilder");
e.printStackTrace();
return false;
}
Method method = null;
try {
method = mConnectivityManager.getClass().getDeclaredMethod("startTethering", int.class, boolean.class, OnStartTetheringCallbackClass(), Handler.class);
if (method == null) {
Log.e(TAG, "startTetheringMethod is null");
} else {
method.invoke(mConnectivityManager, ConnectivityManager.TYPE_MOBILE, false, proxy, null);
Log.d(TAG, "startTethering invoked");
}
return true;
} catch (Exception e) {
Log.e(TAG, "Error in enableTethering");
e.printStackTrace();
}
return false;
}
public void stopTethering() {
try {
Method method = mConnectivityManager.getClass().getDeclaredMethod("stopTethering", int.class);
if (method == null) {
Log.e(TAG, "stopTetheringMethod is null");
} else {
method.invoke(mConnectivityManager, ConnectivityManager.TYPE_MOBILE);
Log.d(TAG, "stopTethering invoked");
}
} catch (Exception e) {
Log.e(TAG, "stopTethering error: " + e.toString());
e.printStackTrace();
}
}
Use above methods to turn on/off Wifi Hotspot with SSID and password defined in the settings.
private int AP_STATE_DISABLED = 11;
private int AP_STATE_ENABLING = 12;
private int AP_STATE_ENABLED = 13;
private int AP_STATE_ERROR = 14;
/**
* #return status hot spot enabled or not
*/
public boolean isHotSpotEnabled(Context context) {
Method method = null;
int actualState = 0;
try {
WifiManager mWifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
method = mWifiManager.getClass().getDeclaredMethod("getWifiApState");
method.setAccessible(true);
actualState = (Integer) method.invoke(mWifiManager, (Object[]) null);
if (actualState == AP_STATE_ENABLING ||actualState == AP_STATE_ENABLED) {
return true;
}
} catch (IllegalArgumentException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
Above method can be used to get the current state of hotspot
I know how to turn on/off wifi hot spot using reflection in android using below method.
private static boolean changeWifiHotspotState(Context context,boolean enable) {
try {
WifiManager manager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
Method method = manager.getClass().getDeclaredMethod("setWifiApEnabled", WifiConfiguration.class,
Boolean.TYPE);
method.setAccessible(true);
WifiConfiguration configuration = enable ? getWifiApConfiguration(manager) : null;
boolean isSuccess = (Boolean) method.invoke(manager, configuration, enable);
return isSuccess;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
But the above method is not working Android 8.0(Oreo).
When I execute above method in Android 8.0, I am getting below statement in logcat.
com.gck.dummy W/WifiManager: com.gck.dummy attempted call to setWifiApEnabled: enabled = true
Is there any other way to on/off hotspot on android 8.0
I thought the LocalOnlyHotspot route was the way to, but as #edsappfactory.com said in the comments - it only gives closed network, no internet access.
In Oreo hot-spotting/tethering moved to ConnectionManager, and its annotated #SystemApi, so (nominally) inaccessible.
As part of something else I was doing, I made an app and put it on github here. It uses reflection to get at the function and DexMaker to generate a subclass of ConnectionManager.OnStartTetheringCallback (which is also inaccessible).
Think it all works okay - bit rough around the edges, so please feel free to make better!
Relevant bits of code are in:
MyOreoWifiManager and;
CallbackMaker
I lost patience trying to get my DexMaker-generated callback to fire the MyOnStartTetheringCallback so all that code is in disarray and commented out.
Finally I got the solution.
Android 8.0, they provided public api to turn on/off hotspot. WifiManager
Below is the code to turn on hotspot
private WifiManager.LocalOnlyHotspotReservation mReservation;
#RequiresApi(api = Build.VERSION_CODES.O)
private void turnOnHotspot() {
WifiManager manager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
manager.startLocalOnlyHotspot(new WifiManager.LocalOnlyHotspotCallback() {
#Override
public void onStarted(WifiManager.LocalOnlyHotspotReservation reservation) {
super.onStarted(reservation);
Log.d(TAG, "Wifi Hotspot is on now");
mReservation = reservation;
}
#Override
public void onStopped() {
super.onStopped();
Log.d(TAG, "onStopped: ");
}
#Override
public void onFailed(int reason) {
super.onFailed(reason);
Log.d(TAG, "onFailed: ");
}
}, new Handler());
}
private void turnOffHotspot() {
if (mReservation != null) {
mReservation.close();
}
}
onStarted(WifiManager.LocalOnlyHotspotReservation reservation) method will be called if hotspot is turned on.. Using WifiManager.LocalOnlyHotspotReservation reference you call close() method to turn off hotspot.
Note:
To turn on hotspot, the Location(GPS) should be enabled in the device. Otherwise, it will throw SecurityException
As per Jon suggestion, I got another way to enable WifiHotSpot in Android Oreo and above.
public boolean enableTetheringNew(MyTetheringCallback callback) {
File outputDir = mContext.getCodeCacheDir();
try {
proxy = ProxyBuilder.forClass(classOnStartTetheringCallback())
.dexCache(outputDir).handler(new InvocationHandler() {
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
switch (method.getName()) {
case "onTetheringStarted":
callback.onTetheringStarted();
break;
case "onTetheringFailed":
callback.onTetheringFailed();
break;
default:
ProxyBuilder.callSuper(proxy, method, args);
}
return null;
}
}).build();
} catch (IOException e) {
e.printStackTrace();
}
ConnectivityManager manager = (ConnectivityManager) mContext.getApplicationContext().getSystemService(ConnectivityManager.class);
Method method = null;
try {
method = manager.getClass().getDeclaredMethod("startTethering", int.class, boolean.class, classOnStartTetheringCallback(), Handler.class);
if (method == null) {
Log.e(TAG, "startTetheringMethod is null");
} else {
method.invoke(manager, TETHERING_WIFI, false, proxy, null);
}
return true;
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return false;
}
private Class classOnStartTetheringCallback() {
try {
return Class.forName("android.net.ConnectivityManager$OnStartTetheringCallback");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
I'm trying to periodically check the network connection. However, this is for a Chinese Android Mini PC, not a tablet or smartphone. I'm using an ethernet to usb adapter instead of Wi-Fi. First I used a broadcastreceiver class:
public class NetworkStateReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getExtras() != null) {
#SuppressWarnings("deprecation")
NetworkInfo eni = (NetworkInfo) intent.getExtras().get(
ConnectivityManager.EXTRA_NETWORK_INFO);
if (eni != null && eni.getState() == NetworkInfo.State.CONNECTED) {
Log.d(TAG, "Network " + eni.getTypeName() + " connected.");
}
}
if (intent.getExtras().getBoolean(
ConnectivityManager.EXTRA_NO_CONNECTIVITY, Boolean.FALSE)) {
Log.d(TAG, "There's no network connectivity.");
}
}
}
This works perfectly for Wi-Fi and mobile. However, for ethernet, there are complications. When I connect the ethernet to usb adapter, it thinks it already has ETHERNET connection, whether the ethernet cable is connected or not. Only when removing the adapter, it knows the ethernet connection was removed.
I tried using a socket, and this kind of works:
private static boolean checkSocket(String host, int port) {
Socket socket = null;
boolean reachable = false;
try {
socket = new Socket(InetAddress.getByName(host), port);
reachable = true;
} catch (UnknownHostException e) {
} catch (IOException e) {
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
}
}
}
return reachable;
}
When there is a connection, it works perfectly and fast. When the connection is lost, it takes way too long for the program to know it has. I need this solution, but it should know way faster that the ethernet connection has been lost. Also, this relies on Exceptions, which I'm not fond of at all.
Lastly I tried a simple ICMP message:
try {
InetAddress address = InetAddress.getByName(host);
if (address.isReachable(timeout)) {
return true;
}
} catch (UnknownHostException e) {
} catch (IOException e) {
}
return false;
This should work, right? Unfortunately, it doesn't. Until now, I've always received a false when executing this code.
What am I doing wrong and what is the correct way to do this?
EDIT 1
I have now tried this solution, which works and doesn't work. It's funny and annoying, as I'm checking this in the onResume(). After a few correct tries, it suddenly stops. I have no idea why though.
boolean reachable = false;
try {
Process p1 = java.lang.Runtime.getRuntime().exec("ping -c 1 " + host);
int retValue = p1.waitFor();
reachable = (retValue == 0);
Log.d(TAG, String.valueOf(reachable));
p1.destroy();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return reachable;
Try connecting to ip addres, not resolving dns as you do
ADD:
Try using ConnectivityManager.getActiveNetworkInfo then
((ConnectivityManager) Context.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo().isConnected()
Is there any way to know (pro grammatically) in your Activity/Application that the user has enabled USB tethering on his phone?
you can also use reflection to access the hidden function for setting usb tethering.
Here is my code.
ConnectivityManager cm =
(ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
Log.d(TAG,"test enable usb tethering");
String[] available = null;
int code=-1;
Method[] wmMethods = cm.getClass().getDeclaredMethods();
for(Method method: wmMethods){
if(method.getName().equals("getTetherableIfaces")){
try {
available = (String[]) method.invoke(cm);
break;
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return;
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return;
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return;
}
}
}
for(Method method: wmMethods){
if(method.getName().equals("tether")){
try {
code = (Integer) method.invoke(cm, available[0]);
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return;
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return;
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return;
}
break;
}
}
if (code==0)
Log.d(TAG,"Enable usb tethering successfully!");
else
Log.d(TAG,"Enable usb tethering failed!");
For disabling usb tethering, you just need to change the reflection method name "getTetherableIfaces" to "getTetheredIfaces", change "tether" to "untether".
Please check.
Looking through the Settings.System documentation points to the answer being no, its not possible to do this.
Link to said documentation
This should work on all phones, confirmed on some Android 7,6 and 5 devices;
Method: interface rndisX (typically rndis0) only shows up when usb tethering is enabled.
Code Example:
private static boolean isTetheringActive(Context context){
try{
for(Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();){
NetworkInterface intf=en.nextElement();
for(Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();){
InetAddress inetAddress=enumIpAddr.nextElement();
if(!intf.isLoopback()){
if(intf.getName().contains("rndis")){
return true;
}
}
}
}
}catch(Exception e){e.printStackTrace();}
return false;
}
Here is a solution to Listen for tethering state changes :
First you need to be familiar with BroadcastReceiver.
you can find a lot of tutorial (google : how to listen for connectivity changes ...)
In order to get the Tethering state update, you need to use a hidden filter action of Android (see ConnectivityManager)
and in your BroadcastReceiver class :
IntentFilter filter = new IntentFilter("android.net.conn.TETHER_STATE_CHANGED");
then register the filter to your BroadcastReceiver :
myApplicationContext.registerReceiver(this, filter);
on your onReceive(final Context context,final Intent intent) method, the Intent.extras information contains 3 arrays filled with the corresponding tethered network interface :
erroredArray / availableArray / activeArray
It's a little bit tricky but you can get the tethering status information.
In addition, you can do some reflexion on hidden function of Android code :
Search for getTetherableIfaces() in the Connectivity Manager.
Here is a link : https://github.com/android/platform_frameworks_base/blob/master/core/java/android/net/ConnectivityManager.java#L1604
You can get the Network Interfaces and check what is active like this:
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
NetworkInterface rndis = null;
NetworkInterface wlan = null;
while(interfaces.hasMoreElements()) {
NetworkInterface nif = interfaces.nextElement();
if(hasIP4Address(nif)) {
if(nif.getDisplayName().startsWith("rndis"))
rndis = nif;
else if (nif.getDisplayName().startsWith("wlan"))
wlan = nif;
}
}
// Let the user choose Wi-Fi or rndis connect
if (rndis != null) {
socket.setNetworkInterface(rndis);
Log.i(TAG, "Subscribe: with interface rndis");
} else if(wlan != null) {
socket.setNetworkInterface(wlan);
Log.i(TAG, "Subscribe: with interface wlan");
}
I have found that if I check for usb0 network interface
it only has an ip address once tethering has been set up.
public static String getIPAddressUsb(final boolean useIPv4) {
try {
final List<NetworkInterface> interfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
for (final NetworkInterface intf : interfaces) {
if (intf.getDisplayName().startsWith("usb")) {
final List<InetAddress> addrs = Collections.list(intf.getInetAddresses());
for (final InetAddress addr : addrs) {
final String sAddr = addr.getHostAddress().toUpperCase();
final boolean isIPv4 = InetAddressUtils.isIPv4Address(sAddr);
if (useIPv4) {
if (isIPv4) { return sAddr; }
} else {
if (!isIPv4) {
final int delim = sAddr.indexOf('%');
return delim < 0 ? sAddr : sAddr.substring(0, delim);
}
}
}
}
}
} catch (final Exception ex) {
// for now eat exceptions
}
return "";
}
boolean isUsbTethered(){
String ipAddr = MipnAndroidApplication.getIPAddressUsb(true);
if (ipAddr.length() == 0) {
Log.i(LOG_TAG, "tethering not enabled");
return false;
} else {
Log.i(LOG_TAG, "tethering enabled :)");
return true;
}
}
How can I start or stop the built-in tethering in Android 2.2 from my application?
There is a non-public Tethering API in the ConnectivityManager. As shown above you can use reflection to access it. I tried this on a number of Android 2.2 phones, and it works on all of them (my HTC turns on tethering but does NOT show this in the status bar..., so check from the other end). Below is some rough code which emits debugging stuff and turns on tethering on usb0.
ConnectivityManager cman = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
Method[] methods = cman.getClass().getDeclaredMethods();
for (Method method : methods) {
if (method.getName().equals("getTetherableIfaces")) {
try {
String[] ifaces = (String[]) method.invoke(cman);
for (String iface : ifaces) {
Log.d("TETHER", "Tether available on " + iface);
}
} catch (Exception e) {
e.printStackTrace();
}
}
if (method.getName().equals("isTetheringSupported")) {
try {
boolean supported = (Boolean) method.invoke(cman);
Log.d("TETHER", "Tether is supported: " + (supported ? "yes" : "no"));
} catch (Exception e) {
e.printStackTrace();
}
}
if (method.getName().equals("tether")) {
Log.d("TETHER", "Starting tether usb0");
try {
int result = (Integer) method.invoke(cman, "usb0");
Log.d("TETHER", "Tether usb0 result: " + result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Please note: this code requires the following permissions to work:
android.permission.ACCESS_NETWORK_STATE
android.permission.CHANGE_NETWORK_STATE
I answered this question here. In short, it is possible, here is the code:
private void setWifiTetheringEnabled(boolean enable) {
WifiManager wifiManager = (WifiManager) getSystemService(WIFI_SERVICE);
Method[] methods = wifiManager.getClass().getDeclaredMethods();
for (Method method : methods) {
if (method.getName().equals("setWifiApEnabled")) {
try {
method.invoke(wifiManager, null, enable);
} catch (Exception ex) {
}
break;
}
}
}
Your app should have the following permission:
android.permission.CHANGE_WIFI_STATE
There are no public APIs in the Android SDK for managing the tethering -- sorry!
I used the code from Android How to turn on hotspot in Android Programmatically! and I enable the portable hotspot for android 4.2. Here's the code.
WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
// TODO Auto-generated method stub
WifiConfiguration wifi_configuration = null;
wifiManager.setWifiEnabled(false);
try
{
//USE REFLECTION TO GET METHOD "SetWifiAPEnabled"
Method method=wifiManager.getClass().getMethod("setWifiApEnabled", WifiConfiguration.class, boolean.class);
method.invoke(wifiManager, wifi_configuration, true);
}
catch (NoSuchMethodException e){
// TODO Auto-generated catch block
e.printStackTrace();
}catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}