Update CtsHostsideNetworkTests to take network capability into account.

Bug: 177641226
Test: atest ./tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
Ignore-AOSP-First: Expedited jobs are not available in AOSP
Change-Id: Ic75ebdc184764b7e7ac02d3e2ca18cbba1c55ee5
This commit is contained in:
Sudheer Shanka 2021-02-18 06:06:50 -08:00
commit 4a96c79495
12 changed files with 147 additions and 67 deletions

View file

@ -23,7 +23,10 @@
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.LocationCheck" />
<target_preparer class="com.android.cts.net.NetworkPolicyTestsPreparer" />
<!-- Enabling change id ALLOW_TEST_API_ACCESS allows that package to access @TestApi methods -->
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="run-command" value="am compat enable ALLOW_TEST_API_ACCESS com.android.cts.net.hostside.app2" />
<option name="teardown-command" value="am compat reset ALLOW_TEST_API_ACCESS com.android.cts.net.hostside.app2" />
<option name="teardown-command" value="cmd power set-mode 0" />
<option name="teardown-command" value="cmd battery reset" />
<option name="teardown-command" value="cmd netpolicy stop-watching" />

View file

@ -17,6 +17,10 @@
package com.android.cts.net.hostside;
interface INetworkStateObserver {
boolean isForeground();
void onNetworkStateChecked(String resultData);
void onNetworkStateChecked(int resultCode, String resultData);
const int RESULT_SUCCESS_NETWORK_STATE_CHECKED = 0;
const int RESULT_ERROR_UNEXPECTED_PROC_STATE = 1;
const int RESULT_ERROR_UNEXPECTED_CAPABILITIES = 2;
const int RESULT_ERROR_OTHER = 3;
}

View file

@ -67,7 +67,7 @@ public class AbstractExpeditedJobTest extends AbstractRestrictBackgroundNetworkT
setRestrictBackground(true);
assertBackgroundNetworkAccess(false);
assertExpeditedJobHasNetworkAccess();
assertExpeditedJobHasNoNetworkAccess();
}
@Test
@ -102,7 +102,7 @@ public class AbstractExpeditedJobTest extends AbstractRestrictBackgroundNetworkT
setRestrictBackground(true);
setBatterySaverMode(true);
assertBackgroundNetworkAccess(false);
assertExpeditedJobHasNetworkAccess();
assertExpeditedJobHasNoNetworkAccess();
}
@Test
@ -114,7 +114,7 @@ public class AbstractExpeditedJobTest extends AbstractRestrictBackgroundNetworkT
setRestrictBackground(true);
setDozeMode(true);
assertBackgroundNetworkAccess(false);
assertExpeditedJobHasNetworkAccess();
assertExpeditedJobHasNoNetworkAccess();
}
@Test
@ -129,6 +129,6 @@ public class AbstractExpeditedJobTest extends AbstractRestrictBackgroundNetworkT
setAppIdle(true);
setDozeMode(true);
assertBackgroundNetworkAccess(false);
assertExpeditedJobHasNetworkAccess();
assertExpeditedJobHasNoNetworkAccess();
}
}

View file

@ -22,6 +22,7 @@ import static android.os.BatteryManager.BATTERY_PLUGGED_USB;
import static android.os.BatteryManager.BATTERY_PLUGGED_WIRELESS;
import static com.android.cts.net.hostside.NetworkPolicyTestUtils.executeShellCommand;
import static com.android.cts.net.hostside.NetworkPolicyTestUtils.forceRunJob;
import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getConnectivityManager;
import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getContext;
import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getInstrumentation;
@ -29,7 +30,6 @@ import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isAppStandbySu
import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isBatterySaverSupported;
import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isDozeModeSupported;
import static com.android.cts.net.hostside.NetworkPolicyTestUtils.restrictBackgroundValueToString;
import static com.android.cts.net.hostside.NetworkPolicyTestUtils.runSatisfiedJob;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@ -56,6 +56,7 @@ import android.os.SystemClock;
import android.provider.DeviceConfig;
import android.service.notification.NotificationListenerService;
import android.util.Log;
import android.util.Pair;
import com.android.compatibility.common.util.BatteryUtils;
import com.android.compatibility.common.util.DeviceConfigStateHelper;
@ -64,6 +65,7 @@ import org.junit.Rule;
import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@ -118,6 +120,7 @@ public abstract class AbstractRestrictBackgroundNetworkTestCase {
private static int PROCESS_STATE_IMPORTANT_FOREGROUND;
private static final String KEY_NETWORK_STATE_OBSERVER = TEST_PKG + ".observer";
private static final String KEY_SKIP_VALIDATION_CHECKS = TEST_PKG + ".skip_validation_checks";
protected static final int TYPE_COMPONENT_ACTIVTIY = 0;
protected static final int TYPE_COMPONENT_FOREGROUND_SERVICE = 1;
@ -290,6 +293,11 @@ public abstract class AbstractRestrictBackgroundNetworkTestCase {
finishExpeditedJob();
}
protected void assertExpeditedJobHasNoNetworkAccess() throws Exception {
launchComponentAndAssertNetworkAccess(TYPE_EXPEDITED_JOB, false);
finishExpeditedJob();
}
protected final void assertBackgroundState() throws Exception {
final int maxTries = 30;
ProcessState state = null;
@ -361,7 +369,7 @@ public abstract class AbstractRestrictBackgroundNetworkTestCase {
for (int i = 1; i <= maxTries; i++) {
error = checkNetworkAccess(expectAvailable);
if (error.isEmpty()) return;
if (error == null) return;
// TODO: ideally, it should retry only when it cannot connect to an external site,
// or no retry at all! But, currently, the initial change fails almost always on
@ -433,7 +441,7 @@ public abstract class AbstractRestrictBackgroundNetworkTestCase {
errors.append("\tnetworkInfo: " + networkInfo + "\n");
errors.append("\tconnectionCheckDetails: " + connectionCheckDetails + "\n");
}
return errors.toString();
return errors.length() == 0 ? null : errors.toString();
}
/**
@ -772,6 +780,11 @@ public abstract class AbstractRestrictBackgroundNetworkTestCase {
}
protected void launchComponentAndAssertNetworkAccess(int type) throws Exception {
launchComponentAndAssertNetworkAccess(type, true);
}
protected void launchComponentAndAssertNetworkAccess(int type, boolean expectAvailable)
throws Exception {
if (type == TYPE_COMPONENT_FOREGROUND_SERVICE) {
startForegroundService();
assertForegroundServiceNetworkAccess();
@ -783,19 +796,27 @@ public abstract class AbstractRestrictBackgroundNetworkTestCase {
final CountDownLatch latch = new CountDownLatch(1);
final Intent launchIntent = getIntentForComponent(type);
final Bundle extras = new Bundle();
final String[] errors = new String[]{null};
extras.putBinder(KEY_NETWORK_STATE_OBSERVER, getNewNetworkStateObserver(latch, errors));
final ArrayList<Pair<Integer, String>> result = new ArrayList<>(1);
extras.putBinder(KEY_NETWORK_STATE_OBSERVER, getNewNetworkStateObserver(latch, result));
extras.putBoolean(KEY_SKIP_VALIDATION_CHECKS, !expectAvailable);
launchIntent.putExtras(extras);
mContext.startActivity(launchIntent);
if (latch.await(ACTIVITY_NETWORK_STATE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
if (!errors[0].isEmpty()) {
if (errors[0] == APP_NOT_FOREGROUND_ERROR) {
// App didn't come to foreground when the activity is started, so try again.
assertForegroundNetworkAccess();
} else {
fail("Network is not available for activity in app2 (" + mUid
+ "): " + errors[0]);
final int resultCode = result.get(0).first;
final String resultData = result.get(0).second;
if (resultCode == INetworkStateObserver.RESULT_SUCCESS_NETWORK_STATE_CHECKED) {
final String error = checkForAvailabilityInResultData(
resultData, expectAvailable);
if (error != null) {
fail("Network is not available for activity in app2 (" + mUid + "): "
+ error);
}
} else if (resultCode == INetworkStateObserver.RESULT_ERROR_UNEXPECTED_PROC_STATE) {
Log.d(TAG, resultData);
// App didn't come to foreground when the activity is started, so try again.
assertForegroundNetworkAccess();
} else {
fail("Unexpected resultCode=" + resultCode + "; received=[" + resultData + "]");
}
} else {
fail("Timed out waiting for network availability status from app2's activity ("
@ -803,20 +824,29 @@ public abstract class AbstractRestrictBackgroundNetworkTestCase {
}
} else if (type == TYPE_EXPEDITED_JOB) {
final Bundle extras = new Bundle();
final String[] errors = new String[]{null};
final ArrayList<Pair<Integer, String>> result = new ArrayList<>(1);
final CountDownLatch latch = new CountDownLatch(1);
extras.putBinder(KEY_NETWORK_STATE_OBSERVER, getNewNetworkStateObserver(latch, errors));
extras.putBinder(KEY_NETWORK_STATE_OBSERVER, getNewNetworkStateObserver(latch, result));
extras.putBoolean(KEY_SKIP_VALIDATION_CHECKS, !expectAvailable);
final JobInfo jobInfo = new JobInfo.Builder(TEST_JOB_ID, TEST_JOB_COMPONENT)
.setExpedited(true)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.setTransientExtras(extras)
.build();
mServiceClient.scheduleJob(jobInfo);
runSatisfiedJob(TEST_APP2_PKG, TEST_JOB_ID);
forceRunJob(TEST_APP2_PKG, TEST_JOB_ID);
if (latch.await(JOB_NETWORK_STATE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
if (!errors[0].isEmpty()) {
fail("Network is not available for expedited job in app2 (" + mUid
+ "): " + errors[0]);
final int resultCode = result.get(0).first;
final String resultData = result.get(0).second;
if (resultCode == INetworkStateObserver.RESULT_SUCCESS_NETWORK_STATE_CHECKED) {
final String error = checkForAvailabilityInResultData(
resultData, expectAvailable);
if (error != null) {
fail("Network is not available for expedited job in app2 (" + mUid + "): "
+ error);
}
} else {
fail("Unexpected resultCode=" + resultCode + "; received=[" + resultData + "]");
}
} else {
fail("Timed out waiting for network availability status from app2's expedited job ("
@ -854,24 +884,11 @@ public abstract class AbstractRestrictBackgroundNetworkTestCase {
}
private Binder getNewNetworkStateObserver(final CountDownLatch latch,
final String[] errors) {
final ArrayList<Pair<Integer, String>> result) {
return new INetworkStateObserver.Stub() {
@Override
public boolean isForeground() {
try {
final ProcessState state = getProcessStateByUid(mUid);
return !isBackground(state.state);
} catch (Exception e) {
Log.d(TAG, "Error while reading the proc state for " + mUid + ": " + e);
return false;
}
}
@Override
public void onNetworkStateChecked(String resultData) {
errors[0] = resultData == null
? APP_NOT_FOREGROUND_ERROR
: checkForAvailabilityInResultData(resultData, true);
public void onNetworkStateChecked(int resultCode, String resultData) {
result.add(Pair.create(resultCode, resultData));
latch.countDown();
}
};

View file

@ -130,9 +130,9 @@ public class NetworkPolicyTestUtils {
return am.isLowRamDevice();
}
/** Asks (not forces) JobScheduler to run the job if constraints are met. */
public static void runSatisfiedJob(String pkg, int jobId) {
executeShellCommand("cmd jobscheduler run -s -u " + UserHandle.myUserId()
/** Forces JobScheduler to run the job if constraints are met. */
public static void forceRunJob(String pkg, int jobId) {
executeShellCommand("cmd jobscheduler run -f -u " + UserHandle.myUserId()
+ " " + pkg + " " + jobId);
}

View file

@ -21,7 +21,7 @@ package {
android_test_helper_app {
name: "CtsHostsideNetworkTestsApp2",
defaults: ["cts_support_defaults"],
sdk_version: "current",
sdk_version: "test_current",
static_libs: ["CtsHostsideNetworkTestsAidl"],
srcs: ["src/**/*.java"],
// Tag this module as a cts test artifact

View file

@ -21,20 +21,23 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
<!--
This application is used to listen to RESTRICT_BACKGROUND_CHANGED intents and store
them in a shared preferences which is then read by the test app. These broadcasts are
handled by 2 listeners, one defined the manifest and another dynamically registered by
a service.
This application is used to listen to RESTRICT_BACKGROUND_CHANGED intents and store
them in a shared preferences which is then read by the test app. These broadcasts are
handled by 2 listeners, one defined the manifest and another dynamically registered by
a service.
The manifest-defined listener also handles ordered broadcasts used to share data with the
test app.
The manifest-defined listener also handles ordered broadcasts used to share data with the
test app.
This application also provides a service, RemoteSocketFactoryService, that the test app can
use to open sockets to remote hosts as a different user ID.
-->
<application android:usesCleartextTraffic="true"
android:testOnly="true">
This application also provides a service, RemoteSocketFactoryService, that the test app can
use to open sockets to remote hosts as a different user ID.
-->
<application android:usesCleartextTraffic="true">
<activity android:name=".MyActivity"
android:exported="true"/>
<service android:name=".MyService"

View file

@ -15,11 +15,13 @@
*/
package com.android.cts.net.hostside.app2;
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
@ -53,6 +55,11 @@ public final class Common {
static final String TEST_PKG = "com.android.cts.net.hostside";
static final String KEY_NETWORK_STATE_OBSERVER = TEST_PKG + ".observer";
static final String KEY_SKIP_VALIDATION_CHECKS = TEST_PKG + ".skip_validation_checks";
static final int TYPE_COMPONENT_ACTIVTY = 0;
static final int TYPE_COMPONENT_FOREGROUND_SERVICE = 1;
static final int TYPE_COMPONENT_EXPEDITED_JOB = 2;
static int getUid(Context context) {
final String packageName = context.getPackageName();
@ -63,15 +70,57 @@ public final class Common {
}
}
static void notifyNetworkStateObserver(Context context, Intent intent) {
private static boolean validateComponentState(Context context, int componentType,
INetworkStateObserver observer) throws RemoteException {
final ActivityManager activityManager = context.getSystemService(ActivityManager.class);
switch (componentType) {
case TYPE_COMPONENT_ACTIVTY: {
final int procState = activityManager.getUidProcessState(Process.myUid());
if (procState != ActivityManager.PROCESS_STATE_TOP) {
observer.onNetworkStateChecked(
INetworkStateObserver.RESULT_ERROR_UNEXPECTED_PROC_STATE,
"Unexpected procstate: " + procState);
return false;
}
return true;
}
case TYPE_COMPONENT_FOREGROUND_SERVICE: {
final int procState = activityManager.getUidProcessState(Process.myUid());
if (procState != ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
observer.onNetworkStateChecked(
INetworkStateObserver.RESULT_ERROR_UNEXPECTED_PROC_STATE,
"Unexpected procstate: " + procState);
return false;
}
return true;
}
case TYPE_COMPONENT_EXPEDITED_JOB: {
final int capabilities = activityManager.getUidProcessCapabilities(Process.myUid());
if ((capabilities & ActivityManager.PROCESS_CAPABILITY_NETWORK) == 0) {
observer.onNetworkStateChecked(
INetworkStateObserver.RESULT_ERROR_UNEXPECTED_CAPABILITIES,
"Unexpected capabilities: " + capabilities);
return false;
}
return true;
}
default: {
observer.onNetworkStateChecked(INetworkStateObserver.RESULT_ERROR_OTHER,
"Unknown component type: " + componentType);
return false;
}
}
}
static void notifyNetworkStateObserver(Context context, Intent intent, int componentType) {
if (intent == null) {
return;
}
final Bundle extras = intent.getExtras();
notifyNetworkStateObserver(context, extras);
notifyNetworkStateObserver(context, extras, componentType);
}
static void notifyNetworkStateObserver(Context context, Bundle extras) {
static void notifyNetworkStateObserver(Context context, Bundle extras, int componentType) {
if (extras == null) {
return;
}
@ -79,17 +128,17 @@ public final class Common {
extras.getBinder(KEY_NETWORK_STATE_OBSERVER));
if (observer != null) {
try {
if (!observer.isForeground()) {
Log.e(TAG, "App didn't come to foreground");
observer.onNetworkStateChecked(null);
final boolean skipValidation = extras.getBoolean(KEY_SKIP_VALIDATION_CHECKS);
if (!skipValidation && !validateComponentState(context, componentType, observer)) {
return;
}
} catch (RemoteException e) {
Log.e(TAG, "Error occurred while reading the proc state: " + e);
Log.e(TAG, "Error occurred while informing the validation result: " + e);
}
AsyncTask.execute(() -> {
try {
observer.onNetworkStateChecked(
INetworkStateObserver.RESULT_SUCCESS_NETWORK_STATE_CHECKED,
MyBroadcastReceiver.checkNetworkStatus(context));
} catch (RemoteException e) {
Log.e(TAG, "Error occurred while notifying the observer: " + e);

View file

@ -18,6 +18,7 @@ package com.android.cts.net.hostside.app2;
import static com.android.cts.net.hostside.app2.Common.ACTION_FINISH_ACTIVITY;
import static com.android.cts.net.hostside.app2.Common.TAG;
import static com.android.cts.net.hostside.app2.Common.TEST_PKG;
import static com.android.cts.net.hostside.app2.Common.TYPE_COMPONENT_ACTIVTY;
import android.app.Activity;
import android.content.BroadcastReceiver;
@ -42,7 +43,7 @@ public class MyActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "MyActivity.onCreate()");
Common.notifyNetworkStateObserver(this, getIntent());
Common.notifyNetworkStateObserver(this, getIntent(), TYPE_COMPONENT_ACTIVTY);
finishCommandReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {

View file

@ -17,6 +17,7 @@ package com.android.cts.net.hostside.app2;
import static com.android.cts.net.hostside.app2.Common.TAG;
import static com.android.cts.net.hostside.app2.Common.TEST_PKG;
import static com.android.cts.net.hostside.app2.Common.TYPE_COMPONENT_FOREGROUND_SERVICE;
import android.R;
import android.app.Notification;
@ -58,7 +59,7 @@ public class MyForegroundService extends Service {
startForeground(42, new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_dialog_alert) // any icon is fine
.build());
Common.notifyNetworkStateObserver(this, intent);
Common.notifyNetworkStateObserver(this, intent, TYPE_COMPONENT_FOREGROUND_SERVICE);
break;
case FLAG_STOP_FOREGROUND:
Log.d(TAG, "Stopping foreground");

View file

@ -17,6 +17,7 @@ package com.android.cts.net.hostside.app2;
import static com.android.cts.net.hostside.app2.Common.ACTION_FINISH_JOB;
import static com.android.cts.net.hostside.app2.Common.TAG;
import static com.android.cts.net.hostside.app2.Common.TYPE_COMPONENT_EXPEDITED_JOB;
import android.app.job.JobParameters;
import android.app.job.JobService;
@ -39,7 +40,8 @@ public class MyJobService extends JobService {
@Override
public boolean onStartJob(JobParameters params) {
Log.v(TAG, "MyJobService.onStartJob()");
Common.notifyNetworkStateObserver(this, params.getTransientExtras());
Common.notifyNetworkStateObserver(this, params.getTransientExtras(),
TYPE_COMPONENT_EXPEDITED_JOB);
mFinishCommandReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {

View file

@ -80,7 +80,7 @@ abstract class HostsideNetworkTestCase extends DeviceTestCase implements IAbiRec
DeviceNotAvailableException {
CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
assertNull(getDevice().installPackage(buildHelper.getTestFile(apk),
false /* reinstall */, true /* grantPermissions */));
false /* reinstall */, true /* grantPermissions */, "-t"));
}
protected void uninstallPackage(String packageName, boolean shouldSucceed)