Merge changes from topic "blocked-reasons-callback-tests" am: 2108a92452 am: 7de5bd543e

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1652214

Change-Id: Ie51249f5fa47b0a5afe287083139082488f9b503
This commit is contained in:
Lorenzo Colitti
2021-03-26 04:36:19 +00:00
committed by Automerger Merge Worker
5 changed files with 155 additions and 38 deletions

View File

@@ -28,10 +28,12 @@ package android.net {
method public void systemReady();
field public static final int BLOCKED_METERED_REASON_ADMIN_DISABLED = 262144; // 0x40000
field public static final int BLOCKED_METERED_REASON_DATA_SAVER = 65536; // 0x10000
field public static final int BLOCKED_METERED_REASON_MASK = -65536; // 0xffff0000
field public static final int BLOCKED_METERED_REASON_USER_RESTRICTED = 131072; // 0x20000
field public static final int BLOCKED_REASON_APP_STANDBY = 4; // 0x4
field public static final int BLOCKED_REASON_BATTERY_SAVER = 1; // 0x1
field public static final int BLOCKED_REASON_DOZE = 2; // 0x2
field public static final int BLOCKED_REASON_LOCKDOWN_VPN = 16; // 0x10
field public static final int BLOCKED_REASON_NONE = 0; // 0x0
field public static final int BLOCKED_REASON_RESTRICTED_MODE = 8; // 0x8
field public static final String PRIVATE_DNS_MODE_OFF = "off";
@@ -41,6 +43,10 @@ package android.net {
field public static final int PROFILE_NETWORK_PREFERENCE_ENTERPRISE = 1; // 0x1
}
public static class ConnectivityManager.NetworkCallback {
method public void onBlockedStatusChanged(@NonNull android.net.Network, int);
}
public class ConnectivitySettingsManager {
method public static void clearGlobalProxy(@NonNull android.content.Context);
method @Nullable public static String getCaptivePortalHttpUrl(@NonNull android.content.Context);

View File

@@ -38,7 +38,9 @@ import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.app.PendingIntent;
import android.app.admin.DevicePolicyManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -871,6 +873,17 @@ public class ConnectivityManager {
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final int BLOCKED_REASON_RESTRICTED_MODE = 1 << 3;
/**
* Flag to indicate that an app is blocked because it is subject to an always-on VPN but the VPN
* is not currently connected.
*
* @see DevicePolicyManager#setAlwaysOnVpnPackage(ComponentName, String, boolean)
*
* @hide
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final int BLOCKED_REASON_LOCKDOWN_VPN = 1 << 4;
/**
* Flag to indicate that an app is subject to Data saver restrictions that would
* result in its metered network access being blocked.
@@ -914,6 +927,14 @@ public class ConnectivityManager {
})
public @interface BlockedReason {}
/**
* Set of blocked reasons that are only applicable on metered networks.
*
* @hide
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final int BLOCKED_METERED_REASON_MASK = 0xffff0000;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
private final IConnectivityManager mService;
@@ -3442,12 +3463,30 @@ public class ConnectivityManager {
* @param blocked Whether access to the {@link Network} is blocked due to system policy.
* @hide
*/
public void onAvailable(@NonNull Network network,
public final void onAvailable(@NonNull Network network,
@NonNull NetworkCapabilities networkCapabilities,
@NonNull LinkProperties linkProperties, boolean blocked) {
@NonNull LinkProperties linkProperties, @BlockedReason int blocked) {
// Internally only this method is called when a new network is available, and
// it calls the callback in the same way and order that older versions used
// to call so as not to change the behavior.
onAvailable(network, networkCapabilities, linkProperties, blocked != 0);
onBlockedStatusChanged(network, blocked);
}
/**
* Legacy variant of onAvailable that takes a boolean blocked reason.
*
* This method has never been public API, but it's not final, so there may be apps that
* implemented it and rely on it being called. Do our best not to break them.
* Note: such apps will also get a second call to onBlockedStatusChanged immediately after
* this method is called. There does not seem to be a way to avoid this.
* TODO: add a compat check to move apps off this method, and eventually stop calling it.
*
* @hide
*/
public void onAvailable(@NonNull Network network,
@NonNull NetworkCapabilities networkCapabilities,
@NonNull LinkProperties linkProperties, boolean blocked) {
onAvailable(network);
if (!networkCapabilities.hasCapability(
NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)) {
@@ -3455,7 +3494,7 @@ public class ConnectivityManager {
}
onCapabilitiesChanged(network, networkCapabilities);
onLinkPropertiesChanged(network, linkProperties);
onBlockedStatusChanged(network, blocked);
// No call to onBlockedStatusChanged here. That is done by the caller.
}
/**
@@ -3619,6 +3658,26 @@ public class ConnectivityManager {
*/
public void onBlockedStatusChanged(@NonNull Network network, boolean blocked) {}
/**
* Called when access to the specified network is blocked or unblocked.
*
* If a NetworkCallback object implements this method,
* {@link #onBlockedStatusChanged(Network, boolean)} will not be called.
*
* <p>Do NOT call {@link #getNetworkCapabilities(Network)} or
* {@link #getLinkProperties(Network)} or other synchronous ConnectivityManager methods in
* this callback as this is prone to race conditions : calling these methods while in a
* callback may return an outdated or even a null object.
*
* @param network The {@link Network} whose blocked status has changed.
* @param blocked The blocked status of this {@link Network}.
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
public void onBlockedStatusChanged(@NonNull Network network, @BlockedReason int blocked) {
onBlockedStatusChanged(network, blocked != 0);
}
private NetworkRequest networkRequest;
private final int mFlags;
}
@@ -3733,7 +3792,7 @@ public class ConnectivityManager {
case CALLBACK_AVAILABLE: {
NetworkCapabilities cap = getObject(message, NetworkCapabilities.class);
LinkProperties lp = getObject(message, LinkProperties.class);
callback.onAvailable(network, cap, lp, message.arg1 != 0);
callback.onAvailable(network, cap, lp, message.arg1);
break;
}
case CALLBACK_LOSING: {
@@ -3767,8 +3826,7 @@ public class ConnectivityManager {
break;
}
case CALLBACK_BLK_CHANGED: {
boolean blocked = message.arg1 != 0;
callback.onBlockedStatusChanged(network, blocked);
callback.onBlockedStatusChanged(network, message.arg1);
}
}
}

View File

@@ -30,6 +30,8 @@ import static android.net.ConnectivityDiagnosticsManager.DataStallReport.DETECTI
import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_DNS_CONSECUTIVE_TIMEOUTS;
import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS;
import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP_PACKET_FAIL_RATE;
import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_MASK;
import static android.net.ConnectivityManager.BLOCKED_REASON_LOCKDOWN_VPN;
import static android.net.ConnectivityManager.BLOCKED_REASON_NONE;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
@@ -108,6 +110,7 @@ import android.net.ConnectionInfo;
import android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
import android.net.ConnectivityDiagnosticsManager.DataStallReport;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.BlockedReason;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.ConnectivityManager.RestrictBackgroundStatus;
import android.net.ConnectivityResources;
@@ -2338,15 +2341,15 @@ public class ConnectivityService extends IConnectivityManager.Stub
private final NetworkPolicyCallback mPolicyCallback = new NetworkPolicyCallback() {
@Override
public void onUidBlockedReasonChanged(int uid, int blockedReasons) {
public void onUidBlockedReasonChanged(int uid, @BlockedReason int blockedReasons) {
mHandler.sendMessage(mHandler.obtainMessage(EVENT_UID_BLOCKED_REASON_CHANGED,
uid, blockedReasons));
}
};
void handleUidBlockedReasonChanged(int uid, int blockedReasons) {
private void handleUidBlockedReasonChanged(int uid, @BlockedReason int blockedReasons) {
maybeNotifyNetworkBlockedForNewState(uid, blockedReasons);
mUidBlockedReasons.put(uid, blockedReasons);
setUidBlockedReasons(uid, blockedReasons);
}
private boolean checkAnyPermissionOf(String... permissions) {
@@ -8052,12 +8055,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
return;
}
final int blockedReasons = mUidBlockedReasons.get(nri.mAsUid, BLOCKED_REASON_NONE);
final boolean metered = nai.networkCapabilities.isMetered();
boolean blocked;
blocked = isUidBlockedByVpn(nri.mAsUid, mVpnBlockedUidRanges);
blocked |= NetworkPolicyManager.isUidBlocked(
mUidBlockedReasons.get(nri.mAsUid, BLOCKED_REASON_NONE), metered);
callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE, blocked ? 1 : 0);
final boolean vpnBlocked = isUidBlockedByVpn(nri.mAsUid, mVpnBlockedUidRanges);
callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE,
getBlockedState(blockedReasons, metered, vpnBlocked));
}
// Notify the requests on this NAI that the network is now lingered.
@@ -8066,6 +8068,21 @@ public class ConnectivityService extends IConnectivityManager.Stub
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING, lingerTime);
}
private static int getBlockedState(int reasons, boolean metered, boolean vpnBlocked) {
if (!metered) reasons &= ~BLOCKED_METERED_REASON_MASK;
return vpnBlocked
? reasons | BLOCKED_REASON_LOCKDOWN_VPN
: reasons & ~BLOCKED_REASON_LOCKDOWN_VPN;
}
private void setUidBlockedReasons(int uid, @BlockedReason int blockedReasons) {
if (blockedReasons == BLOCKED_REASON_NONE) {
mUidBlockedReasons.delete(uid);
} else {
mUidBlockedReasons.put(uid, blockedReasons);
}
}
/**
* Notify of the blocked state apps with a registered callback matching a given NAI.
*
@@ -8073,7 +8090,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
* any given nai, all requests need to be considered according to the uid who filed it.
*
* @param nai The target NetworkAgentInfo.
* @param oldMetered True if the previous network capabilities is metered.
* @param oldMetered True if the previous network capabilities were metered.
* @param newMetered True if the current network capabilities are metered.
* @param oldBlockedUidRanges list of UID ranges previously blocked by lockdown VPN.
* @param newBlockedUidRanges list of UID ranges blocked by lockdown VPN.
*/
private void maybeNotifyNetworkBlocked(NetworkAgentInfo nai, boolean oldMetered,
boolean newMetered, List<UidRange> oldBlockedUidRanges,
@@ -8082,22 +8102,18 @@ public class ConnectivityService extends IConnectivityManager.Stub
for (int i = 0; i < nai.numNetworkRequests(); i++) {
NetworkRequest nr = nai.requestAt(i);
NetworkRequestInfo nri = mNetworkRequests.get(nr);
final boolean oldBlocked, newBlocked, oldVpnBlocked, newVpnBlocked;
oldVpnBlocked = isUidBlockedByVpn(nri.mAsUid, oldBlockedUidRanges);
newVpnBlocked = (oldBlockedUidRanges != newBlockedUidRanges)
final int blockedReasons = mUidBlockedReasons.get(nri.mAsUid, BLOCKED_REASON_NONE);
final boolean oldVpnBlocked = isUidBlockedByVpn(nri.mAsUid, oldBlockedUidRanges);
final boolean newVpnBlocked = (oldBlockedUidRanges != newBlockedUidRanges)
? isUidBlockedByVpn(nri.mAsUid, newBlockedUidRanges)
: oldVpnBlocked;
final int blockedReasons = mUidBlockedReasons.get(nri.mAsUid, BLOCKED_REASON_NONE);
oldBlocked = oldVpnBlocked || NetworkPolicyManager.isUidBlocked(
blockedReasons, oldMetered);
newBlocked = newVpnBlocked || NetworkPolicyManager.isUidBlocked(
blockedReasons, newMetered);
if (oldBlocked != newBlocked) {
final int oldBlockedState = getBlockedState(blockedReasons, oldMetered, oldVpnBlocked);
final int newBlockedState = getBlockedState(blockedReasons, newMetered, newVpnBlocked);
if (oldBlockedState != newBlockedState) {
callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_BLK_CHANGED,
encodeBool(newBlocked));
newBlockedState);
}
}
}
@@ -8107,25 +8123,23 @@ public class ConnectivityService extends IConnectivityManager.Stub
* @param uid The uid for which the rules changed.
* @param blockedReasons The reasons for why an uid is blocked.
*/
private void maybeNotifyNetworkBlockedForNewState(int uid, int blockedReasons) {
private void maybeNotifyNetworkBlockedForNewState(int uid, @BlockedReason int blockedReasons) {
for (final NetworkAgentInfo nai : mNetworkAgentInfos) {
final boolean metered = nai.networkCapabilities.isMetered();
final boolean vpnBlocked = isUidBlockedByVpn(uid, mVpnBlockedUidRanges);
final boolean oldBlocked, newBlocked;
oldBlocked = vpnBlocked || NetworkPolicyManager.isUidBlocked(
mUidBlockedReasons.get(uid, BLOCKED_REASON_NONE), metered);
newBlocked = vpnBlocked || NetworkPolicyManager.isUidBlocked(
blockedReasons, metered);
if (oldBlocked == newBlocked) {
final int oldBlockedState = getBlockedState(
mUidBlockedReasons.get(uid, BLOCKED_REASON_NONE), metered, vpnBlocked);
final int newBlockedState = getBlockedState(blockedReasons, metered, vpnBlocked);
if (oldBlockedState == newBlockedState) {
continue;
}
final int arg = encodeBool(newBlocked);
for (int i = 0; i < nai.numNetworkRequests(); i++) {
NetworkRequest nr = nai.requestAt(i);
NetworkRequestInfo nri = mNetworkRequests.get(nr);
if (nri != null && nri.mAsUid == uid) {
callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_BLK_CHANGED, arg);
callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_BLK_CHANGED,
newBlockedState);
}
}
}

View File

@@ -41,10 +41,10 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;

View File

@@ -7277,6 +7277,20 @@ public class ConnectivityServiceTest {
mMockVpn.disconnect();
}
private class DetailedBlockedStatusCallback extends TestNetworkCallback {
public void expectAvailableThenValidatedCallbacks(HasNetwork n, int blockedStatus) {
super.expectAvailableThenValidatedCallbacks(n.getNetwork(), blockedStatus, TIMEOUT_MS);
}
public void expectBlockedStatusCallback(HasNetwork n, int blockedStatus) {
// This doesn't work:
// super.expectBlockedStatusCallback(blockedStatus, n.getNetwork());
super.expectBlockedStatusCallback(blockedStatus, n.getNetwork(), TIMEOUT_MS);
}
public void onBlockedStatusChanged(Network network, int blockedReasons) {
getHistory().add(new CallbackEntry.BlockedStatusInt(network, blockedReasons));
}
}
@Test
public void testNetworkBlockedStatus() throws Exception {
final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
@@ -7284,11 +7298,16 @@ public class ConnectivityServiceTest {
.addTransportType(TRANSPORT_CELLULAR)
.build();
mCm.registerNetworkCallback(cellRequest, cellNetworkCallback);
final DetailedBlockedStatusCallback detailedCallback = new DetailedBlockedStatusCallback();
mCm.registerNetworkCallback(cellRequest, detailedCallback);
mockUidNetworkingBlocked();
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
detailedCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent,
BLOCKED_REASON_NONE);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
@@ -7296,17 +7315,23 @@ public class ConnectivityServiceTest {
setBlockedReasonChanged(BLOCKED_REASON_BATTERY_SAVER);
cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent,
BLOCKED_REASON_BATTERY_SAVER);
assertNull(mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
assertExtraInfoFromCmBlocked(mCellNetworkAgent);
// ConnectivityService should cache it not to invoke the callback again.
// If blocked state does not change but blocked reason does, the boolean callback is called.
// TODO: investigate de-duplicating.
setBlockedReasonChanged(BLOCKED_METERED_REASON_USER_RESTRICTED);
cellNetworkCallback.assertNoCallback();
cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent,
BLOCKED_METERED_REASON_USER_RESTRICTED);
setBlockedReasonChanged(BLOCKED_REASON_NONE);
cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, BLOCKED_REASON_NONE);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
@@ -7314,6 +7339,8 @@ public class ConnectivityServiceTest {
setBlockedReasonChanged(BLOCKED_METERED_REASON_DATA_SAVER);
cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent,
BLOCKED_METERED_REASON_DATA_SAVER);
assertNull(mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
@@ -7323,6 +7350,8 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
cellNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_METERED, mCellNetworkAgent);
cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
detailedCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_METERED, mCellNetworkAgent);
detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, BLOCKED_REASON_NONE);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
@@ -7332,6 +7361,10 @@ public class ConnectivityServiceTest {
cellNetworkCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED,
mCellNetworkAgent);
cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
detailedCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED,
mCellNetworkAgent);
detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent,
BLOCKED_METERED_REASON_DATA_SAVER);
assertNull(mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
@@ -7339,6 +7372,7 @@ public class ConnectivityServiceTest {
setBlockedReasonChanged(BLOCKED_REASON_NONE);
cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, BLOCKED_REASON_NONE);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
@@ -7346,10 +7380,13 @@ public class ConnectivityServiceTest {
setBlockedReasonChanged(BLOCKED_REASON_NONE);
cellNetworkCallback.assertNoCallback();
detailedCallback.assertNoCallback();
// Restrict background data. Networking is not blocked because the network is unmetered.
setBlockedReasonChanged(BLOCKED_METERED_REASON_DATA_SAVER);
cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent,
BLOCKED_METERED_REASON_DATA_SAVER);
assertNull(mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
@@ -7359,12 +7396,14 @@ public class ConnectivityServiceTest {
setBlockedReasonChanged(BLOCKED_REASON_NONE);
cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, BLOCKED_REASON_NONE);
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
assertExtraInfoFromCmPresent(mCellNetworkAgent);
setBlockedReasonChanged(BLOCKED_REASON_NONE);
cellNetworkCallback.assertNoCallback();
detailedCallback.assertNoCallback();
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);