Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor network connection setting on Android #865

Merged
merged 8 commits into from
Apr 12, 2018
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import io.appium.java_client.FindsByAndroidUIAutomator;
import io.appium.java_client.LocksDevice;
import io.appium.java_client.PressesKeyCode;
import io.appium.java_client.android.connection.HasNetworkConnection;
import io.appium.java_client.remote.MobilePlatform;
import io.appium.java_client.screenrecording.CanRecordScreen;
import io.appium.java_client.service.local.AppiumDriverLocalService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,13 +197,12 @@ public class AndroidMobileCommandHelper extends MobileCommand {
/**
* This method forms a {@link Map} of parameters for the setting of device network connection.
*
* @param connection The bitmask of the desired connection
* @param bitMask The bitmask of the desired connection
* @return a key-value pair. The key is the command name. The value is a {@link Map} command arguments.
*/
public static Map.Entry<String, Map<String, ?>> setConnectionCommand(Connection connection) {
public static Map.Entry<String, Map<String, ?>> setConnectionCommand(int bitMask) {
String[] parameters = new String[] {"name", "parameters"};
Object[] values =
new Object[] {"network_connection", ImmutableMap.of("type", connection.bitMask)};
Object[] values = new Object[] {"network_connection", ImmutableMap.of("type", bitMask)};
return new AbstractMap.SimpleEntry<>(
SET_NETWORK_CONNECTION, prepareArguments(parameters, values));
}
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/io/appium/java_client/android/Connection.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@

/**
* for use with setting Network Connections on a mobile device.
* @deprecated Use {@link io.appium.java_client.android.connection.ConnectionState} instead
*/
@Deprecated
public enum Connection {
NONE(0),
AIRPLANE(1),
Expand All @@ -31,4 +33,8 @@ public enum Connection {
Connection(int bitMask) {
this.bitMask = bitMask;
}

public int getBitMask() {
return bitMask;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.appium.java_client.android.connection;

public class ConnectionState {
public static final int AIRPLANE_MODE_MASK = 0b001;
public static final int WIFI_MASK = 0b010;
public static final int DATA_MASK = 0b100;

private final int bitMask;

public int getBitMask() {
return bitMask;
}

public ConnectionState(int bitMask) {
this.bitMask = bitMask;
}

/**
* @return true if airplane mode is enabled.
*/
public boolean isAirplaneModeEnabled() {
return (bitMask & AIRPLANE_MODE_MASK) != 0;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that all these bitwise operations can be simplified via java.utils.BitSet

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you find the current implementation not readable enough? I think BitSet usage might add an unnecessary overhead, since the actual count of bitwise operations here is pretty limited anyway

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, np. However from my point of view bs.set(AIRPLANE_MODE_BIT, false) looks more naturaly than this.bitMask = bitMask & ~AIRPLANE_MODE_MASK; (Maybe I just don't like bitwise logic :)

}

/**
* @return true if Wi-Fi connection is enabled.
*/
public boolean isWiFiEnabled() {
return (bitMask & WIFI_MASK) != 0;
}

/**
* @return true if data connection is enabled.
*/
public boolean isDataEnabled() {
return (bitMask & DATA_MASK) != 0;
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.appium.java_client.android.connection;

import static io.appium.java_client.android.connection.ConnectionState.AIRPLANE_MODE_MASK;
import static io.appium.java_client.android.connection.ConnectionState.DATA_MASK;
import static io.appium.java_client.android.connection.ConnectionState.WIFI_MASK;

public class ConnectionStateBuilder {
private int bitMask;

/**
* Initializes connection state builder with the default value (all off).
*/
public ConnectionStateBuilder() {
this.bitMask = 0;
}

/**
* Initializes connection state builder with the the predefined bit mask.
* This constructor might be handy to change an existing connection state.
*
* @param bitMask the actual initial state bit mask to set
*/
public ConnectionStateBuilder(int bitMask) {
this.bitMask = bitMask;
}

/**
* Initializes connection state builder with the the predefined bit mask.
* This constructor might be handy to change an existing connection state.
*
* @param state the actual initial state to set
*/
public ConnectionStateBuilder(ConnectionState state) {
this(state.getBitMask());
}

/**
* Sets airplane mode to enabled state if it was disabled.
* This option only works up to Android 6.
* Enabling the airplane mode on the device will automatically
* disable Wi-Fi and data connections.
*
* @return self instance for chaining
*/
public ConnectionStateBuilder withAirplaneModeEnabled() {
this.bitMask = bitMask | AIRPLANE_MODE_MASK;
return this;
}

/**
* Sets airplane mode to disabled state if it was enabled.
* This option only works up to Android 6.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If i remember right, even data and wifi through set/get connection wasn't working with Android 7. Also i thought retrieving connection state was only possible through shell. may be i'm wrong. Let me try this today.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wifi should work. for data there is a comment, that it only works on emulator and on rooted devices

*
* @return self instance for chaining
*/
public ConnectionStateBuilder withAirplaneModeDisabled() {
this.bitMask = bitMask & ~AIRPLANE_MODE_MASK;
return this;
}

/**
* Sets Wi-Fi connection mode to enabled state if it was disabled.
*
* @return self instance for chaining
*/
public ConnectionStateBuilder withWiFiEnabled() {
this.bitMask = bitMask | WIFI_MASK;
return this;
}

/**
* Sets Wi-Fi connection mode to disabled state if it was enabled.
*
* @return self instance for chaining
*/
public ConnectionStateBuilder withWiFiDisabled() {
this.bitMask = bitMask & ~WIFI_MASK;
return this;
}

/**
* Sets data connection mode to enabled state if it was disabled.
* This option only works on rooted devices or on emulators.
*
* @return self instance for chaining
*/
public ConnectionStateBuilder withDataEnabled() {
this.bitMask = bitMask | DATA_MASK;
return this;
}

/**
* Sets data connection mode to disabled state if it was enabled.
* This option only works on rooted devices or on emulators.
*
* @return self instance for chaining
*/
public ConnectionStateBuilder withDataDisabled() {
this.bitMask = bitMask & ~DATA_MASK;
return this;
}

/**
* Builds connection state instance, which is ready to be passed as Appium server parameter.
*
* @return ConnectionState instance
*/
public ConnectionState build() {
return new ConnectionState(bitMask);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,43 +14,43 @@
* limitations under the License.
*/

package io.appium.java_client.android;
package io.appium.java_client.android.connection;

import static io.appium.java_client.android.AndroidMobileCommandHelper.getNetworkConnectionCommand;
import static io.appium.java_client.android.AndroidMobileCommandHelper.setConnectionCommand;

import io.appium.java_client.CommandExecutionHelper;
import io.appium.java_client.ExecutesMethod;
import org.openqa.selenium.WebDriverException;
import io.appium.java_client.android.Connection;

public interface HasNetworkConnection extends ExecutesMethod {

/**
* Set the network connection of the device.
*
* @deprecated use {@link #setConnection(ConnectionState)} instead
* @param connection The bitmask of the desired connection
*/
@Deprecated
default void setConnection(Connection connection) {
CommandExecutionHelper.execute(this, setConnectionCommand(connection));
CommandExecutionHelper.execute(this, setConnectionCommand(connection.getBitMask()));
}

/**
* Set the network connection of the device.
*
* @param connection The bitmask of the desired connection
*/
default void setConnection(ConnectionState connection) {
CommandExecutionHelper.execute(this, setConnectionCommand(connection.getBitMask()));
}

/**
* Get the current network settings of the device.
*
* @return Connection object will let you inspect the status
* of None, AirplaneMode, Wifi, Data and All connections
*/
default Connection getConnection() {
long bitMask = CommandExecutionHelper.execute(this, getNetworkConnectionCommand());
Connection[] types = Connection.values();

for (Connection connection: types) {
if (connection.bitMask == bitMask) {
return connection;
}
}
throw new WebDriverException("The unknown network connection "
+ "type has been returned. The bitmask is " + bitMask);
default ConnectionState getConnection() {
return new ConnectionState(CommandExecutionHelper.execute(this, getNetworkConnectionCommand()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@

package io.appium.java_client.android;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import io.appium.java_client.android.connection.ConnectionState;
import io.appium.java_client.android.connection.ConnectionStateBuilder;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
Expand All @@ -26,23 +28,32 @@
public class AndroidConnectionTest extends BaseAndroidTest {

@Test public void test1() {
driver.setConnection(Connection.WIFI);
assertEquals(Connection.WIFI,
driver.getConnection());
driver.setConnection(new ConnectionStateBuilder()
.withWiFiEnabled()
.build());
assertTrue(driver.getConnection().isWiFiEnabled());
}

@Test public void test2() {
driver.setConnection(Connection.NONE);
assertEquals(Connection.NONE,
driver.getConnection());
driver.setConnection(Connection.AIRPLANE);
assertEquals(Connection.AIRPLANE,
driver.getConnection());
driver.setConnection(new ConnectionStateBuilder()
.withAirplaneModeDisabled()
.build());
ConnectionState state = driver.getConnection();
assertTrue(!state.isAirplaneModeEnabled() && !state.isWiFiEnabled() && !state.isDataEnabled());
Copy link
Member

@SrinivasanTarget SrinivasanTarget Apr 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check for wifi and data is not necessary i believe as disabling airplane mode will only disable airplane mode incase if it was already enabled and leave's the rest of connection as is. Isn't it?

Enabling airplane mode will disable both data and wifi but disabling airplane will not disable/enable rest. Correct me if i'm wrong.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here also we might need to remove the additional checks.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These I'd prefer to keep

driver.setConnection(new ConnectionStateBuilder()
.withAirplaneModeEnabled()
.build());
state = driver.getConnection();
assertTrue(state.isAirplaneModeEnabled() && !state.isWiFiEnabled() && !state.isDataEnabled());
}

@Test public void test3() {
driver.setConnection(Connection.ALL);
assertEquals(Connection.ALL,
driver.getConnection());
driver.setConnection(new ConnectionStateBuilder()
.withAirplaneModeDisabled()
.withWiFiEnabled()
.withDataEnabled()
.build());
ConnectionState state = driver.getConnection();
assertTrue(!state.isAirplaneModeEnabled() && state.isWiFiEnabled() && state.isDataEnabled());
}
}