Android Wifi flow - android

I am trying to trace the wifi settings code. My intention is to know the flow from application to kernel layer after toggling the WIFI button OFF to ON.
While we go to Settings page in Android, toggle the WLAN(WIFI) button, then
your wifi should be enabled.
I found that this page corresponds to WifiSettings.java. In this file, while you toggle the button from OFF to ON:
private void updateWifiState(int state) {
getActivity().invalidateOptionsMenu();
switch (state) {
case WifiManager.WIFI_STATE_ENABLING:
addMessagePreference(R.string.wifi_starting);
break;
}
mLastInfo = null;
mLastState = null;
mScanner.pause();
}
This function will be called.
I then go to check WifiManager.java. I found:
/**
* Wi-Fi is currently being enabled. The state will change to
{#link#WIFI_STATE_ENABLED}
if it finishes successfully.
*
* #see #WIFI_STATE_CHANGED_ACTION
* #see #getWifiState()
*/
public static final int WIFI_STATE_ENABLING = 2;
However, after this, I did not really understand how to dig deeper in tracing the flow.

You should look at WifiEnabler.java, since the code you mentioned just shows some string.
In WifiEnabler::onCheckChanged(), you can see that mWifiManager.setWifiEnabled() is called.
After that, you may look at WifiManager.java and other related files in that directory. There is a state machine and you should trace the state transitions.
Generally, Android is using wpa_supplicant, which is nearly the same as Linux. The configuration file is at /data/misc/wifi/wpa_supplicant.conf, and is generated by Android. After you toggle the wifi switch, Android starts wpa_supplicant and communicates with it. The wpa_supplicant is in charge of scanning and connecting to wifi station.

Related

Questions on creating a service that connects automatically to a BLE device on android

I am implementing a service that uses the autoconnect feature of bluetoothGatt to connect to the device and monitor it while it is being connected.
I work on the assumption that the device is already bonded (a coworker is responsible for that part) so autoconnect should not have any problems
my code is as follows:
//the callback is for the class I have created that actually does the connection
class BTService: Service(), CoroutineScope, BTConnection.Callback {
private val btReceiver by lazy { BluetoothStateReceiver(this::btStateChange) } //receiver for bt adapter changes
private var connection:BTConnection? = null
private var readJob:Job? = null
override fun onCreate() {
buildNotificationChannels()
registerReceiver(btReceiver, IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)) //since I can't register this receiver in AndroidManifest any more I did it here
}
private fun btStateChange(enabled: Boolean) {
if (enabled)
startConnecting()
else
stopConnection()
}
private fun startConnecting() {
val address = prefs.address //get the current saved address
val current = connection //get the current connection
//try to stop the current connection if it is different than the one we want to set up
if (current != null && !current.address.equals(address, true))
current.stop()
if (address.isNullOrBlank())
return
//then we create a new connection if needed
val new = if (current == null || !current.address.equals(address, true)) {
Injections.buildConnection(application, address, this)
} else {
current
}
connection = new
new.connect()
}
//this is one of the callbacks from BTConnection.Callback
override fun connected(address: String) {
if (address != connection?.address) return
val cn = connection ?: return
showConnectionNotification()
val notification = buildForegroundNotification()
startForeground(FOREGROUND_ID, notification)
readJob?.cancel()
readJob = launch {
cn.dataFlow //this is a flow that will be emmitting read data
.cancellable()
.flowOn(Dispatchers.IO)
.buffer()
.onEach(this#BTService::parseData)
.flowOn(Dispatchers.Default)
}
}
private suspend fun parseData(bytes:ByteArray) { //this is where the parsing and storage etc happens
}
private fun stopConnection() {
val cn = connection
connection = null
cn?.stop()
}
override fun disconnected(address: String) { //another callback from the connection class
showDisconnectNotification()
stopForeground(true)
}
my code that stops the connection is
fun stop() {
canceled = true
if (connected)
gatt?.disconnect()
launch(Dispatchers.IO) {
delay(1000)
gatt?.close()
gatt = null
}
}
my code is based (and affected) by this really good article I read:
https://medium.com/#martijn.van.welie/making-android-ble-work-part-2-47a3cdaade07
I have also created a receiver for boot events that will call
context.startService(Intent(context, BTService::class.java))
just to make sure that the service is created at least once and the bt receiver is registered
my questions are:
a) is there a chance that my service will be destroyed while it is not in foreground mode? i.e. when the device is not near by and bluetoothGat.connect is suspending while autoconnecting? is it enough for me to return START_STICKY from onStartCommand() to make sure that even when my service is destroyed it will start again?
b) if there is such a case, is there a way to at least recreate the service so the btReceiver is at least registered?
c) when should close() be called on bluetoothGatt in case of autoconnect = true? only when creating a new connection (in my example where I call Injections.buildConnection)? do I also call it when the bluetoothadapter is disabled? or can I reuse the same connection and bluetoothGatt if the user turns the bluetooth adapter off and on again?
d) is there a way to find out if autoconnect has failed and will not try again? and is there a way to actually test and reproduce such an effect? the article mentioned above says it can happen when the batteries of the peripheral are almost empty, or when you are on the edge of the Bluetooth range
thanks in advance for any help you can provide
a-b) If your app does not have an activity or a service that is in the foreground, the system may kill it at anytime. Pending or active BLE connections doesn't affect the system's point of view when to kill the app whatsoever. (When it comes to scanning for advertisements, the story is completely different though.)
The general approach to make sure autoConnects stay alive is to have a foreground service running at all the time. So don't stop it while the device is currently not connected, if you want to have a pending connection. There is no point in using Job Scheduler, WorkManagers etc. since having a foreground service should be enough to keep the app process alive, and pending/active connections are kept alive as long as the app is. The app does not use any cpu% at all when waiting for pending BLE connections. However some Chinese phone makers are known to not follow the Android documentation, by sometimes killing apps even though they have running foreground services.
c) Each BluetoothGatt object represents and refers to an object inside the Bluetooth process running on the same phone. By default the system allows a total of 32 such objects (last time I checked). In order to release these precious resources, you call close(). If you forget, you will have a leak, meaning your app or some other app might not be able to create a BluetoothGatt object. (When app processes exit, their BluetoothGatt objects are however closed automatically). The API is a bit strangely designed, that there is both a disconnect method and a close method. But anyway, the disconnect method gracefully initiates a disconnection of the connection and you will then get an onConnectionStateChange callback telling when the disconnection is complete. You must however call close in order to free the resource, or call connect if you'd like to re-connect, or you can take an action a bit later. Calling close on a connected BluetoothGatt object will also disconnect, but you won't get any callback due to the object is being destroyed at the same time.
Since all BluetoothGatt objects represents objects in the Bluetooth process, these will "die" or stop working when you turn off Bluetooth, since that involves shutting down the Bluetooth process. This means you need to recreate all BluetoothGatt objects when Bluetooth is restarted. You can call close on the old objects, but it won't do anything since they're dead. Since the documentation doesn't say anything about this, I suggest you call close anyway to be on the safe side if the behaviour is changed in the future.
d) To detect if a connectGatt call fails and will not try again, you can listen to the onConnectionStateChange callback. If this gives an error code, such as 257, it usually means that the system has reached maximum number of connections, or maximum number of some resource. You can test this out by simply initiating pending connections to a bunch of different Bluetooth device addresses.
I would not trust the statement that new connection attempts would be aborted if the peripheral is low on battery or being on the "edge of Bluetooth range". I'd be glad to see a pin point to Android's Bluetooth source code where this happens, since I really believe this is not true at all.
First of all, if you are intending to distribute your app to Google Play Store, you need to be targeting minimum api level 29 if I'm not mistaken, hence you should be using either JobService along with JobScheduler or WorkManager, instead of Service. This is to support the background limitations from Oreo(26) onwards.
a) if you properly implement any of the two options I mentioned above, you can write a proper service that will not terminate unless you stop it. Here are some resources on JobService : (resource1, resource2, resource3)
b) You can re-register as you please upon the onStartJob() method of your JobService, which will recreate your app.
c) Each time you are done with the peripheral ble device, you need to close the gatt connection with it. Here is a snippet from the BluetoothGatt class
/**
* Close this Bluetooth GATT client.
*
* Application should call this method as early as possible after it is done with
* this GATT client.
*/
public void close() {
Also, from the BluetoothAdapter class javadoc, you can see that all the connections are terminated gracefully when ble is disabled.
/**
* Turn off the local Bluetooth adapter—do not use without explicit
* user action to turn off Bluetooth.
* <p>This gracefully shuts down all Bluetooth connections, stops Bluetooth
* system services, and powers down the underlying Bluetooth hardware.
* <p class="caution"><strong>Bluetooth should never be disabled without
* direct user consent</strong>. The {#link #disable()} method is
* provided only for applications that include a user interface for changing
* system settings, such as a "power manager" app.</p>
* <p>This is an asynchronous call: it will return immediately, and
* clients should listen for {#link #ACTION_STATE_CHANGED}
* to be notified of subsequent adapter state changes. If this call returns
* true, then the adapter state will immediately transition from {#link
* #STATE_ON} to {#link #STATE_TURNING_OFF}, and some time
* later transition to either {#link #STATE_OFF} or {#link
* #STATE_ON}. If this call returns false then there was an
* immediate problem that will prevent the adapter from being turned off -
* such as the adapter already being turned off.
*
* #return true to indicate adapter shutdown has begun, or false on immediate error
*/
#RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
public boolean disable() {
d) I am not sure on what callback will be triggered. To reproduce, the two items you mentioned seem like valid cases to try.
I hope this helps you perfect your project!

Android Instrumentation Test Offline Cases

For my instrumentation tests I am using Robotium. Mostly I am able to test everything but offline cases.
As soon as I disable data (using adb, F8 shortcut in emulator, etc. ...) the test disconnects. It goes on in the device/emulator but no results are reported.
So, I have got an idea to put just the app in offline mode and not the whole device. The problem is I don't know how...
Using iptablesApi I would need to root my device. I have read that Mobiwol app uses some kind of a VPN to restrict apps internet access without the need of rooting a device.
Question
How does Mobiwol app blocks the internet connection per application? Or is there another way how to test apks offline?
EDIT 12/30/2014
I forgot to say that I am able to run tests offline but I have to start tests when the device is in offline state. Currently, I divided my tests into OFFLINE and ONLINE ones. After running ONLINEs I execute the famous adb kill-server and adb start-server. After that I execute OFFLINEs.
Just making a few suggestions since there seem to be different questions here.
1) If all you want to do is turn off the data before running the OFFLINE test case you might want to simply try using robotium itself to do so..
Example:
For WiFi:
WifiManager wifi=(WifiManager)solo.getCurrentActivity().getSystemService(Context.WIFI_SERVICE);
wifi.setWifiEnabled(false);
For Mobile Data(using reflections):
ConnectivityManager dataManager=(ConnectivityManager)solo.getCurrentActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
Method dataClass = ConnectivityManager.class.getDeclaredMethod(“setMobileDataEnabled”, boolean.class);
dataClass.setAccessible(true);
dataClass.invoke(dataManager, true);
You can do the two above calls in the setup() method before running the individual test case in the OFFLINE suite.
Once all the test case in the OFFLINE suite are done with you can enable the WiFi/DATA back on in the teardown() method at the very end.
2) Looking at the app that you posted in the OP, it seems pretty much that it:
Uses the ipTables based on the OS version
Creates a script header based on the UID's for all the applications
that need WiFi/Data
Should be getting the list of installed apps on the device along with
any hidden apps etc from the package manager.
And again executes scripts based on user selection for black list and
overrides the existing rules in the ipTable with the user desired
rules.
Pretty sure though must have been quite hard to code all of that..Sounds much easier in the form of bullet points.
Hope this helps you somewhat.
P.S: If you do figure out something please post an updated answer, would like to know how did you make it work
Update: Make sure you have the neccessary permissions for setting the WiFi/Data on/off in your application manifest. NOT the test apk manifest. IT HAS TO BE THE APPLICATION MANIFEST ITSELF.
There is this library which might help you. Its an extension to solo. http://adventuresinqa.com/2014/02/17/extsolo-library-to-extend-your-robotium-test-automation/
After spending hours trying to do it similar to user2511882s solution, I still had an exception, because of missing permissions (yes the "modify system settings" permission was activated).
I ended up doing it with UI automator:
public static void setAirplaneMode(boolean enable)
{
if ((enable ? 1 : 0) == Settings.System.getInt(getInstrumentation().getContext().getContentResolver(),
Settings.Global.AIRPLANE_MODE_ON, 0))
{
return;
}
UiDevice device = UiDevice.getInstance(getInstrumentation());
device.openQuickSettings();
// Find the text of your language
BySelector description = By.desc("Airplane mode");
// Need to wait for the button, as the opening of quick settings is animated.
device.wait(Until.hasObject(description), 500);
device.findObject(description).click();
getInstrumentation().getContext().sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
}
You will need the ACCESS_NETWORK_STATE in your androidTest manifest file:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
Don't forget to disable it after the test.
If you have other languages than english, you need to change "Airplane mode" to the text of your language. As I have several translations, I read it from a ressource string.
There is great library from LinkedIn Test Butler, you can enable, disable both WiFi and mobile data by simply calling:
TestButler.setGsmState(false);
TestButler.setWifiState(false);
The main advantage of this library is that it does not require any permission in your manifest, for more details please refer to project website:
https://github.com/linkedin/test-butler
Sorry if I'm oversimplifying this, but what about just putting the phone/emulator in airplane mode? Through the actual user interface. That's what I do to test offline cases.
Here is a solution that uses UiAutomator to enable or disable "Aeroplane mode" from the drop-down status bar, which turns off all networking if enabled. It works on most Android OS versions, and it uses parts of the answer from user Aorlinn (12 October 2019).
app/build.gradle
dependencies {
androidTestImplementation "androidx.test.uiautomator:uiautomator:2.2.0"
}
AndroidManifest.xml
No extra permissions are needed, not even <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> , because UiAutomator is only clicking on a button in the status bar UI. Therefore the app does not need direct access to modify the device's Wifi or mobile data settings.
Kotlin
import android.os.Build
import android.provider.Settings
import androidx.test.espresso.matcher.ViewMatchers.assertThat
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.UiObjectNotFoundException
import androidx.test.uiautomator.Until
import org.hamcrest.Matchers.`is`
import org.junit.Assert
import org.junit.Assume.assumeNoException
import org.junit.Assume.assumeThat
import java.io.IOException
private var airplaneModeOn = 0
private val OFF = 0
private val ON = 1
private var airplaneModeButtonPosition: Point? = null
/**
* Turn off the Internet connectivity by switching "Aeroplane mode" (flight mode) on. From:
* https://stackoverflow.com/questions/27620976/android-instrumentation-test-offline-cases#68956544
*/
#Test
fun checkAppWillWork_withNoInternetConnection() {
try {
airplaneModeOn = getCurrentAirplaneModeSetting()
} catch (e: Exception) {
e.printStackTrace()
assumeNoException("Cannot retrieve device setting of 'Airplane Mode'. Aborting the test.", e)
}
// Check that Airplane mode is off
assertThat(airplaneModeOn, `is`(OFF))
// Press the "Aeroplane mode" button on the 'Quick Settings' panel
toggleAeroplaneModeButton()
// Verify Airplane mode is on
assumeThat("Cannot change the 'Aeroplane Mode' setting. Test aborted.", airplaneModeOn, `is`(ON))
assertThat(airplaneModeOn, `is`(ON))
// Do some tests when the Internet is down
// Note: Switch the Internet back on in cleanup()
}
/**
* Turn the Internet connectivity on or off by pressing the "Aeroplane mode" button. It opens the
* status bar at the top, then drags it down to reveal the buttons on the 'Quick Settings' panel.
*/
#Throws(UiObjectNotFoundException::class)
private fun toggleAeroplaneModeButton() {
try {
airplaneModeOn = getCurrentAirplaneModeSetting()
} catch (e: SecurityException) {
e.printStackTrace()
Assert.fail()
} catch (e: IOException) {
e.printStackTrace()
Assert.fail()
}
// Open the status bar at the top; drag it down to reveal the buttons
val device = UiDevice.getInstance(getInstrumentation())
device.openQuickSettings()
// Wait for the button to be visible, because opening the Quick Settings is animated.
// You can use any string here; You only need a time delay wait here.
val description = By.desc("AeroplaneMode")
device.wait(Until.hasObject(description), 2000)
// Search for and click the button
var buttonClicked = clickObjectIfFound(device,
"Aeroplane mode", "Airplane mode", "機内モード", "Modo avión")
if (!buttonClicked) {
// Swipe the Quick Panel window to the LEFT, if possible
val screenWidth = device.displayWidth
val screenHeight = device.displayHeight
device.swipe((screenWidth * 0.80).toInt(), (screenHeight * 0.30).toInt(),
(screenWidth * 0.20).toInt(), (screenHeight * 0.30).toInt(), 50)
buttonClicked = clickObjectIfFound(device,
"Aeroplane mode", "Airplane mode", "機内モード", "Modo avión")
}
if (!buttonClicked) {
// Swipe the Quick Panel window to the RIGHT, if possible
val screenWidth = device.displayWidth
val screenHeight = device.displayHeight
device.swipe((screenWidth * 0.20).toInt(), (screenHeight * 0.30).toInt(),
(screenWidth * 0.80).toInt(), (screenHeight * 0.30).toInt(), 50)
clickObjectIfFound(device,
"Aeroplane mode", "Airplane mode", "機内モード", "Modo avión")
}
// Wait for the Internet to disconnect or re-connect
device.wait(Until.hasObject(description), 6000)
// Close the Quick Settings panel
getInstrumentation().context
.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
// Verify change in device settings
try {
airplaneModeOn = getCurrentAirplaneModeSetting()
} catch (e: SecurityException) {
e.printStackTrace()
Assert.fail()
} catch (e: IOException) {
e.printStackTrace()
Assert.fail()
}
}
/**
* On Android 8/9, use an 'adb shell' command to retrieve the "Airplane Mode" setting, like this:
*
*
* `adb shell settings list global | grep airplane_mode_on ==> "airplane_mode_on=0"`
*
*
* (But note that grep is not available on the Android shell. See guidance URLs below)
*
*
* * https://www.reddit.com/r/tasker/comments/fbi5ai/psa_you_can_use_adb_to_find_all_the_settings_that/
* * https://stackoverflow.com/questions/33970956/test-if-soft-keyboard-is-visible-using-espresso
*
*
* On all other Android OS versions, use `Settings.System.getInt()` to retrieve the "Airplane Mode" setting.
* It sets `airplaneModeOn = 1 (true)` or `airplaneModeOn = 0 (false)`
*
* #throws IOException if `executeShellCommand()` didn't work
* #throws SecurityException if `Settings.System.getInt()` didn't work
*/
#Throws(IOException::class, SecurityException::class)
private fun getCurrentAirplaneModeSetting(): Int {
if (Build.VERSION.SDK_INT in 26..28 /*Android 8-9*/) {
val shellResponse = UiDevice
.getInstance(getInstrumentation())
.executeShellCommand("settings list global")
airplaneModeOn = when {
shellResponse.contains("airplane_mode_on=1") -> 1
shellResponse.contains("airplane_mode_on=0") -> 0
else -> throw IOException("Unsuitable response from adb shell command 'settings list global'")
}
} else {
// Oddly this causes a SecurityException on Android 8,9 devices
airplaneModeOn = Settings.System.getInt(
getInstrumentation().context.contentResolver,
Settings.Global.AIRPLANE_MODE_ON,
0)
}
return airplaneModeOn
}
/**
* Make UiAutomator search for and click a single button, based on its text label.
*
* Sometimes multiple buttons will match the required text label. For example,
* when Airplane mode is switched on, "Mobile data Aeroplane mode" and "Aeroplane mode"
* are 2 separate buttons on the Quick Settings panel, on Android 10+.
* For Aeroplane mode, always click on the last matched item.
*
* #param textLabels You must supply the language variants of the button that you want to click,
* for example "Cancel" (English), "Cancelar" (Spanish), "취소" (Korean)
* #return True if a button was found and clicked, otherwise return false
*/
private fun clickObjectIfFound(device: UiDevice, vararg textLabels: String): Boolean {
for (languageVariant in textLabels) {
val availableButtons = device.findObjects(By.text(languageVariant))
if (availableButtons.size >= 1) {
if (airplaneModeButtonPosition == null) {
availableButtons[availableButtons.size - 1].click()
airplaneModeButtonPosition = availableButtons[availableButtons.size - 1].visibleCenter
return true
} else {
// Use the stored position to avoid clicking on the wrong button
for (button in availableButtons) {
if (button.visibleCenter == airplaneModeButtonPosition) {
button.click()
airplaneModeButtonPosition = null
return true
}
}
}
}
}
return false
}
#After
fun cleanup() {
// Switch the Internet connectivity back on, for other tests.
if (airplaneModeOn == ON) {
toggleAeroplaneModeButton()
assertThat(airplaneModeOn, `is`(OFF))
}
}
You will have to adjust the string "Aeroplane mode" if your device is in a different language to English. For example, you could check for about 70 language translations here: https://github.com/aosp-mirror/platform_frameworks_base/search?q=global_actions_toggle_airplane_mode
Due to #user2511882 answer you can use Application context instead of Activity in Android X Test via:
internal fun switchWifi(value: Boolean) {
val wifiManager = ApplicationProvider.getApplicationContext<YourApplicationClass>().getSystemService(Context.WIFI_SERVICE) as WifiManager
wifiManager.isWifiEnabled = value}
Consider that this approach only works on API <= 28. There are other approaches like using UI Automator or API > 28

How do I keep a ChromeCast route alive when my app is in the background on battery power?

I'm working on an Android app that supports sending music to a ChromeCast. We'd like users to be able to cast entire music playlists while the app runs in the background.
When my Nexus 7 is not connected to USB power and I turn the screen inactivity timeout to 15 seconds in the settings, the app will disconnect from the ChromeCast about 90 seconds after the device powers off its screen.
I've identified that I'm getting a MediaRouter.Callback call to onRouteUnselected, and since that's the callback I get when a user disconnects from a route, I'm handling it by tearing down the ApplicationSession.
When I plug back in and check the logcat, I see this message around the same time:
I/MediaRouter(19970): Choosing a new selected route because the current one is no longer selectable: MediaRouter.RouteInfo{ uniqueId=... }
Can I do anything to avoid the route being unselected when the app is in the background, or is there something else I can do to get the behavior I want?
I eventually got around this by refusing to disconnect the message streams and tear down the session when the route was disconnected under these conditions, and silently re-select the route when it became available again. The route gets deselected, but it does not affect my casting session.
To do this, I check to see if the route exists when it's unselected.
public void onRouteUnselected(final MediaRouter router, final RouteInfo route) {
if (!onUiThread()) {
new Handler(Looper.getMainLooper()).post((new Runnable() {
#Override
public void run() {
onRouteUnselected(router, route);
}
}));
return;
}
boolean isThisRouteAvailable = doesRouterContainRoute(router, route);
mRouteToReconnectTo = null;
if (isThisRouteAvailable) {
// Perform code to close the message streams and tear down the session.
} else {
// The route was unselected because it's no longer available from the router,
// so try to just keep playing until the message streams get disconnected.
mRouteToReconnectTo = route;
// Short-circuited a disconnect.
}
}
Later, when the route comes back, we can immediately re-select it.
#Override
public void onRouteAdded(MediaRouter router, RouteInfo route) {
super.onRouteAdded(router, route);
// if mRouteToReconnectTo is not null, check to see if this route
// matches it, and reconnect if it does with router.selectRoute(route)
}
#Override
public void onRouteSelected(final MediaRouter router, final RouteInfo route) {
if (!onUiThread()) {
new Handler(Looper.getMainLooper()).post((new Runnable() {
#Override
public void run() {
onRouteSelected(router, route);
}
}));
return;
}
if (areRoutesEqual(mRouteToReconnectTo, route)) {
// Short-circuited a reconnect.
mRouteToReconnectTo = null;
return;
}
mRouteToReconnectTo = null;
// Standard post-selection stuff goes here
}
There's no good way to compare two RouteInfo's, so I ended up writing a helper function that compared their description strings.
Rooster's answer is perfectly feasible and actually provides good insight as to how to re-connect to a route once it comes back online....
but....just to give further insight on what's going on....
You're getting...
I/MediaRouter(19970): Choosing a new selected route because the current one is no longer selectable: MediaRouter.RouteInfo{ uniqueId=... }
because when the device goes to sleep and is NOT plugged into a power source, the WIFI hardware is going into a low-power profile mode (and possibly shutting down entirely). This results in packet loss and subsequently causes the MedaRouter to fire the onRouteUnselected callback.
To prevent the Wifi from turning off you could set a WakeLock on the Wifi in the following manner:
WifiLock wifiLock;
WifiManager wm = (WifiManager) getSystemService(Context.WIFI_SERVICE);
wifiLock = wm.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF , "MyWifiLock");
wifiLock.acquire();
Using the flag WifiManager.WIFI_MODE_FULL_HIGH_PERF will keep the WIFI hardware alive and active when the device goes to sleep. Caution, this flag is only available to API 12 and above.
I tried using the WifiManager.WIFI_MODE_FULL flag when creating the WifiLock, but that didn't seem to do the trick.
Obviously anyone using any type of WifiLock or WakeLock should take considerable care in making sure locks released when no longer needed. Also, beware this will cause battery drain when the device screen is off.
If you used the sample code (Android in this case), you're probably doing this...
mSession.setStopApplicationWhenEnding(true);
mSession.endSession();
...when the route is unselected. If you instead do this...
mSession.setStopApplicationWhenEnding(false);
mSession.endSession();
...then you can clean up the session, but the Chromecast will keep the application alive. When the route becomes available again (or possibly when the user picks the device again) you can build a new session. I have yet to explore how to determine if the new session is talking to a "brand new" instance of the application or to the application left running from another session, but I'll update this answer when I do.

how to register a device driver with android/linux power driver?

I am working on a lcd related kernel level device driver for a android device. Can someone please help me with power management related APIs in Android through which I can register my driver to recieve any changes in device state.
To be more precise I want my driver to recieve any events from power driver(or is there something else) in case when the system state changes, for example from running state to sleep state or suspend state. I would want my driver also to sleep which in current scenario is eating up all the battery..
I have got few links http://www.kandroid.org/online-pdk/guide/power_management.html
and http://developer.android.com/reference/android/os/PowerManager.html
which talks about functions like android_register_early_suspend and android_register_early_resume but I could not find any call to these functions in my entire kernel source code (based on linux 3.0)
I have heard of "Linux Power Management" and can find folders like drivers/base/power and drivers/power in my current baseline code. How can I use this driver in my code.
Please let me know if I am missing with any details.
Any help would be highly appreciated.
Your Driver should implement Runtime suspend/resume callbacks. Check this doc.
sample code to implement Device PM callbacks in 'struct dev_pm_ops' (as defined in linux/pm.h)
static int lcdxxx_suspend(struct device *dev)
{
//Your Code to suspend your device
}
static int lcdxxx_resume(struct device *dev)
{
//Your code to resume your device
}
static const struct dev_pm_ops lcd_pm_ops = {
.suspend = lcdxxx_suspend,
.resume = lcdxxx_resume,
};
struct device_driver lcd_driver = {
....
.pm = &lcd_pm_ops,
....
}
This is just sample impl. Real Implementation depends on your requirements.

Android 4.0 4G toggle

This is for the Verizon LTE version of the Samsung Galaxy Nexus.
I am tasked with writing a tiny app that will effectively disable/enable 4G capability. This can be done manually via settings > mobile network > network mode and choosing either LTE/CDMA (4g enabled) or CDMA (3g only).
I have not tried anything yet because Android development isn't my strong suit. I am looking for guidance... examples, code samples etc. I am assuming this should almost be a one-liner, but it has been my experience that with Android development nothing is as simple as it appears.
Any help will be greatly appreciated.
There is a preference in the Settings.Secure class that is hidden from the SDK:
/**
* The preferred network mode 7 = Global
* 6 = EvDo only
* 5 = CDMA w/o EvDo
* 4 = CDMA / EvDo auto
* 3 = GSM / WCDMA auto
* 2 = WCDMA only
* 1 = GSM only
* 0 = GSM / WCDMA preferred
* #hide
*/
public static final String PREFERRED_NETWORK_MODE =
"preferred_network_mode";
You could use Reflection on this or just localize the constant to your project. The problem with this is that you cannot change the value of this setting (as with all secure settings), you can only read it. The aforementioned values are not the only possible ones, there are actually a few more located in com.android.internal.telephony.RILConstants, which is again hidden from the SDK and would require Reflection to access.
There is another hidden method in TelephonyManager, but again it is read only there is no other method for setting this constant. This would tell you exactly what you want to know, whether the device is set to "LTE/ CDMA" (LTE_ON_CDMA_TRUE) or "CDMA only" (LTE_ON_CDMA_FALSE):
/**
* Return if the current radio is LTE on CDMA. This
* is a tri-state return value as for a period of time
* the mode may be unknown.
*
* #return {#link Phone#LTE_ON_CDMA_UNKNOWN}, {#link Phone#LTE_ON_CDMA_FALSE}
* or {#link Phone#LTE_ON_CDMA_TRUE}
*
* #hide
*/
public int getLteOnCdmaMode() {
try {
return getITelephony().getLteOnCdmaMode();
} catch (RemoteException ex) {
// Assume no ICC card if remote exception which shouldn't happen
return Phone.LTE_ON_CDMA_UNKNOWN;
} catch (NullPointerException ex) {
// This could happen before phone restarts due to crashing
return Phone.LTE_ON_CDMA_UNKNOWN;
}
}
From my research you could not make such an application without root access and using something like setprop from the command line, but even then you may need to restart the entire Telephony process in order for this setting to take effect.
Finally, if you are still interested see com.android.phone.Settings to see how the system handles this toggle. It is rather elaborate, and as I mentioned would require permissions that a normal Android application would not be granted.
I'm also interested in changing the settings WCDMA-only, WCDMA/LTE, ...
I found the way to change Settings.secure.* with root privilege as is shown the below.
new ExecuteAsRootBase() {
#Override
protected ArrayList<String> getCommandsToExecute() {
ArrayList<String> cmds = new ArrayList<String>();
cmds.add("su -c 'chmod 755 "+mySqlite+"'");
cmds.add("echo \"UPDATE secure SET value='"+ value +"' WHERE name='"+ key +"'; \" | "+mySqlite+" /data/data/com.android.providers.settings/databases/settings.db");
//TODO: SQL injection can be done!!!
return cmds;
}
}.execute();
ExecuteAsRootBase is introduced here, and mySqlite is "/data/data/"+context.getPackageName()+"/files/sqlite3" where sqlite3 is put in advance.
However, it seems that we have to call com.android.internal.telephony.Phone.setPreferredNetworkType() for switching (WCDMA only<=>WCDMA/LTE) after setting Settings.secure.PREFERRED_NETWORK_MODE.
My phone (even set Settings.secure.PREFERRED_NETWORK_MODE = 2) attached to LTE network...
All the other answers are correct that this requires access to Settings.Secure. Take a look at how the phone app handles this setting https://github.com/dzo/packages_apps_phone/blob/master/src/com/android/phone/Use2GOnlyCheckBoxPreference.java
or take a look at the Toggle2G app source:
https://github.com/TheMasterBaron/Toggle-2G
http://developer.android.com/reference/android/provider/Settings.System.html
Aside from writing the code in your Activity.java, you will probably have to ask for permission to access these settings in the AndroidManifest.xml. So it's annoying but should be simple enough.

Categories

Resources