ConnOnActivityStartTest.java revision cb2b2ef73cd245b61292d2f8b524dc2da40df99e
1/* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.net; 18 19import static android.util.DebugUtils.valueToString; 20 21import static org.junit.Assert.assertEquals; 22import static org.junit.Assert.assertFalse; 23import static org.junit.Assert.assertNotNull; 24import static org.junit.Assert.assertTrue; 25import static org.junit.Assert.fail; 26 27import com.android.frameworks.servicestests.R; 28import com.android.servicestests.aidl.INetworkStateObserver; 29 30import android.app.PendingIntent; 31import android.content.BroadcastReceiver; 32import android.content.ComponentName; 33import android.content.Context; 34import android.content.Intent; 35import android.content.IntentFilter; 36import android.content.IntentSender; 37import android.content.pm.IPackageDeleteObserver; 38import android.content.pm.PackageInstaller; 39import android.content.pm.PackageManager; 40import android.net.ConnectivityManager; 41import android.net.NetworkInfo; 42import android.net.Uri; 43import android.os.BatteryManager; 44import android.os.Bundle; 45import android.os.RemoteException; 46import android.os.SystemClock; 47import android.support.test.InstrumentationRegistry; 48import android.support.test.filters.LargeTest; 49import android.support.test.runner.AndroidJUnit4; 50import android.support.test.uiautomator.UiDevice; 51import android.util.Log; 52 53import libcore.io.IoUtils; 54 55import org.junit.AfterClass; 56import org.junit.BeforeClass; 57import org.junit.Ignore; 58import org.junit.Test; 59import org.junit.runner.RunWith; 60 61import java.io.IOException; 62import java.io.InputStream; 63import java.io.OutputStream; 64import java.util.concurrent.CountDownLatch; 65import java.util.concurrent.TimeUnit; 66 67/** 68 * Tests for verifying network availability on activity start. 69 * 70 * To run the tests, use 71 * 72 * runtest -c com.android.server.net.ConnOnActivityStartTest frameworks-services 73 * 74 * or the following steps: 75 * 76 * Build: m FrameworksServicesTests 77 * Install: adb install -r \ 78 * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk 79 * Run: adb shell am instrument -e class com.android.server.net.ConnOnActivityStartTest -w \ 80 * com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner 81 */ 82@Ignore 83@LargeTest 84@RunWith(AndroidJUnit4.class) 85public class ConnOnActivityStartTest { 86 private static final String TAG = ConnOnActivityStartTest.class.getSimpleName(); 87 88 private static final String ACTION_INSTALL_COMPLETE = "com.android.server.net.INSTALL_COMPLETE"; 89 90 private static final String TEST_APP_URI = 91 "android.resource://com.android.frameworks.servicestests/raw/conntestapp"; 92 private static final String TEST_PKG = "com.android.servicestests.apps.conntestapp"; 93 private static final String TEST_ACTIVITY_CLASS = TEST_PKG + ".ConnTestActivity"; 94 95 private static final String ACTION_FINISH_ACTIVITY = TEST_PKG + ".FINISH"; 96 97 private static final String EXTRA_NETWORK_STATE_OBSERVER = TEST_PKG + ".observer"; 98 99 private static final long BATTERY_OFF_TIMEOUT_MS = 2000; // 2 sec 100 private static final long BATTERY_OFF_CHECK_INTERVAL_MS = 200; // 0.2 sec 101 102 private static final long WAIT_FOR_INSTALL_TIMEOUT_MS = 2000; // 2 sec 103 104 private static final long NETWORK_CHECK_TIMEOUT_MS = 6000; // 6 sec 105 106 private static final long SCREEN_ON_DELAY_MS = 500; // 0.5 sec 107 108 private static final String NETWORK_STATUS_SEPARATOR = "\\|"; 109 110 private static final int REPEAT_TEST_COUNT = 5; 111 112 private static Context mContext; 113 private static UiDevice mUiDevice; 114 private static int mTestPkgUid; 115 private static BatteryManager mBatteryManager; 116 private static ConnectivityManager mConnectivityManager; 117 118 @BeforeClass 119 public static void setUpOnce() throws Exception { 120 mContext = InstrumentationRegistry.getContext(); 121 mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); 122 123 installAppAndAssertInstalled(); 124 mContext.getPackageManager().setApplicationEnabledSetting(TEST_PKG, 125 PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0); 126 mTestPkgUid = mContext.getPackageManager().getPackageUid(TEST_PKG, 0); 127 128 mBatteryManager = (BatteryManager) mContext.getSystemService(Context.BATTERY_SERVICE); 129 mConnectivityManager = (ConnectivityManager) mContext.getSystemService( 130 Context.CONNECTIVITY_SERVICE); 131 } 132 133 @AfterClass 134 public static void tearDownOnce() { 135 mContext.getPackageManager().deletePackage(TEST_PKG, 136 new IPackageDeleteObserver.Stub() { 137 @Override 138 public void packageDeleted(String packageName, int returnCode) 139 throws RemoteException { 140 Log.e(TAG, packageName + " deleted, returnCode: " + returnCode); 141 } 142 }, 0); 143 } 144 145 @Test 146 public void testStartActivity_batterySaver() throws Exception { 147 if (!isNetworkAvailable()) { 148 fail("Device doesn't have network connectivity"); 149 } 150 setBatterySaverMode(true); 151 try { 152 testConnOnActivityStart("testStartActivity_batterySaver"); 153 } finally { 154 setBatterySaverMode(false); 155 } 156 } 157 158 @Test 159 public void testStartActivity_dataSaver() throws Exception { 160 if (!isNetworkAvailable()) { 161 fail("Device doesn't have network connectivity"); 162 } 163 setDataSaverMode(true); 164 try { 165 testConnOnActivityStart("testStartActivity_dataSaver"); 166 } finally { 167 setDataSaverMode(false); 168 } 169 } 170 171 @Test 172 public void testStartActivity_dozeMode() throws Exception { 173 if (!isNetworkAvailable()) { 174 fail("Device doesn't have network connectivity"); 175 } 176 setDozeMode(true); 177 try { 178 testConnOnActivityStart("testStartActivity_dozeMode"); 179 } finally { 180 setDozeMode(false); 181 } 182 } 183 184 @Test 185 public void testStartActivity_appStandby() throws Exception { 186 if (!isNetworkAvailable()) { 187 fail("Device doesn't have network connectivity"); 188 } 189 try{ 190 turnBatteryOff(); 191 setAppIdle(true); 192 SystemClock.sleep(30000); 193 turnScreenOn(); 194 startActivityAndCheckNetworkAccess(); 195 } finally { 196 turnBatteryOn(); 197 setAppIdle(false); 198 } 199 } 200 201 @Test 202 public void testStartActivity_backgroundRestrict() throws Exception { 203 if (!isNetworkAvailable()) { 204 fail("Device doesn't have network connectivity"); 205 } 206 updateRestrictBackgroundBlacklist(true); 207 try { 208 testConnOnActivityStart("testStartActivity_backgroundRestrict"); 209 } finally { 210 updateRestrictBackgroundBlacklist(false); 211 } 212 } 213 214 private void testConnOnActivityStart(String testName) throws Exception { 215 for (int i = 1; i <= REPEAT_TEST_COUNT; ++i) { 216 try { 217 Log.d(TAG, testName + " Start #" + i); 218 turnScreenOn(); 219 SystemClock.sleep(SCREEN_ON_DELAY_MS); 220 startActivityAndCheckNetworkAccess(); 221 Log.d(TAG, testName + " end #" + i); 222 } finally { 223 finishActivity(); 224 } 225 } 226 } 227 228 // TODO: Some of these methods are also used in CTS, so instead of duplicating code, 229 // create a static library which can be used by both servicestests and cts. 230 private void setBatterySaverMode(boolean enabled) throws Exception { 231 if (enabled) { 232 turnBatteryOff(); 233 executeCommand("settings put global low_power 1"); 234 } else { 235 executeCommand("settings put global low_power 0"); 236 turnBatteryOn(); 237 } 238 final String result = executeCommand("settings get global low_power"); 239 assertEquals(enabled ? "1" : "0", result); 240 } 241 242 private void setDataSaverMode(boolean enabled) throws Exception { 243 executeCommand("cmd netpolicy set restrict-background " + enabled); 244 final String output = executeCommand("cmd netpolicy get restrict-background"); 245 final String expectedSuffix = enabled ? "enabled" : "disabled"; 246 assertTrue("output '" + output + "' should end with '" + expectedSuffix + "'", 247 output.endsWith(expectedSuffix)); 248 } 249 250 private void setDozeMode(boolean enabled) throws Exception { 251 if (enabled) { 252 turnBatteryOff(); 253 turnScreenOff(); 254 executeCommand("dumpsys deviceidle force-idle deep"); 255 } else { 256 turnScreenOn(); 257 turnBatteryOn(); 258 executeCommand("dumpsys deviceidle unforce"); 259 } 260 assertDelayedCommandResult("dumpsys deviceidle get deep", enabled ? "IDLE" : "ACTIVE", 261 5 /* maxTries */, 500 /* napTimeMs */); 262 } 263 264 private void setAppIdle(boolean enabled) throws Exception { 265 executeCommand("am set-inactive " + TEST_PKG + " " + enabled); 266 assertDelayedCommandResult("am get-inactive " + TEST_PKG, "Idle=" + enabled, 267 10 /* maxTries */, 2000 /* napTimeMs */); 268 } 269 270 private void updateRestrictBackgroundBlacklist(boolean add) throws Exception { 271 if (add) { 272 executeCommand("cmd netpolicy add restrict-background-blacklist " + mTestPkgUid); 273 } else { 274 executeCommand("cmd netpolicy remove restrict-background-blacklist " + mTestPkgUid); 275 } 276 assertRestrictBackground("restrict-background-blacklist", mTestPkgUid, add); 277 } 278 279 private void assertRestrictBackground(String list, int uid, boolean expected) throws Exception { 280 final int maxTries = 5; 281 boolean actual = false; 282 final String expectedUid = Integer.toString(uid); 283 String uids = ""; 284 for (int i = 1; i <= maxTries; i++) { 285 final String output = executeCommand("cmd netpolicy list " + list); 286 uids = output.split(":")[1]; 287 for (String candidate : uids.split(" ")) { 288 actual = candidate.trim().equals(expectedUid); 289 if (expected == actual) { 290 return; 291 } 292 } 293 Log.v(TAG, list + " check for uid " + uid + " doesn't match yet (expected " 294 + expected + ", got " + actual + "); sleeping 1s before polling again"); 295 SystemClock.sleep(1000); 296 } 297 fail(list + " check for uid " + uid + " failed: expected " + expected + ", got " + actual 298 + ". Full list: " + uids); 299 } 300 301 private void turnBatteryOff() throws Exception { 302 executeCommand("cmd battery unplug"); 303 assertBatteryOff(); 304 } 305 306 private void assertBatteryOff() throws Exception { 307 final long endTime = SystemClock.uptimeMillis() + BATTERY_OFF_TIMEOUT_MS; 308 while (mBatteryManager.isCharging() && SystemClock.uptimeMillis() < endTime) { 309 SystemClock.sleep(BATTERY_OFF_CHECK_INTERVAL_MS); 310 } 311 assertFalse("Power should be disconnected", mBatteryManager.isCharging()); 312 } 313 314 private void turnBatteryOn() throws Exception { 315 executeCommand("cmd battery reset"); 316 } 317 318 private void turnScreenOff() throws Exception { 319 executeCommand("input keyevent KEYCODE_SLEEP"); 320 } 321 322 private void turnScreenOn() throws Exception { 323 executeCommand("input keyevent KEYCODE_WAKEUP"); 324 executeCommand("wm dismiss-keyguard"); 325 } 326 327 private String executeCommand(String cmd) throws IOException { 328 final String result = mUiDevice.executeShellCommand(cmd).trim(); 329 Log.d(TAG, String.format("Result for '%s': %s", cmd, result)); 330 return result; 331 } 332 333 private void assertDelayedCommandResult(String cmd, String expectedResult, 334 int maxTries, int napTimeMs) throws IOException { 335 String result = ""; 336 for (int i = 1; i <= maxTries; ++i) { 337 result = executeCommand(cmd); 338 if (expectedResult.equals(result)) { 339 return; 340 } 341 Log.v(TAG, "Command '" + cmd + "' returned '" + result + " instead of '" 342 + expectedResult + "' on attempt #" + i 343 + "; sleeping " + napTimeMs + "ms before trying again"); 344 SystemClock.sleep(napTimeMs); 345 } 346 fail("Command '" + cmd + "' did not return '" + expectedResult + "' after " 347 + maxTries + " attempts. Last result: '" + result + "'"); 348 } 349 350 private boolean isNetworkAvailable() throws Exception { 351 final NetworkInfo networkInfo = mConnectivityManager.getActiveNetworkInfo(); 352 return networkInfo != null && networkInfo.isConnected(); 353 } 354 355 private void startActivityAndCheckNetworkAccess() throws Exception { 356 final CountDownLatch latch = new CountDownLatch(1); 357 final Intent launchIntent = new Intent().setComponent( 358 new ComponentName(TEST_PKG, TEST_ACTIVITY_CLASS)); 359 final Bundle extras = new Bundle(); 360 final String[] errors = new String[] {null}; 361 extras.putBinder(EXTRA_NETWORK_STATE_OBSERVER, new INetworkStateObserver.Stub() { 362 @Override 363 public void onNetworkStateChecked(String resultData) { 364 errors[0] = checkForAvailability(resultData); 365 latch.countDown(); 366 } 367 }); 368 launchIntent.putExtras(extras); 369 mContext.startActivity(launchIntent); 370 if (latch.await(NETWORK_CHECK_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { 371 if (!errors[0].isEmpty()) { 372 fail("Network not available for test app " + mTestPkgUid); 373 } 374 } else { 375 fail("Timed out waiting for network availability status from test app " + mTestPkgUid); 376 } 377 } 378 379 private void finishActivity() { 380 final Intent finishIntent = new Intent(ACTION_FINISH_ACTIVITY) 381 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 382 mContext.sendBroadcast(finishIntent); 383 } 384 385 private String checkForAvailability(String resultData) { 386 if (resultData == null) { 387 assertNotNull("Network status from app2 is null, Uid: " + mTestPkgUid, resultData); 388 } 389 // Network status format is described on MyBroadcastReceiver.checkNetworkStatus() 390 final String[] parts = resultData.split(NETWORK_STATUS_SEPARATOR); 391 assertEquals("Wrong network status: " + resultData + ", Uid: " + mTestPkgUid, 392 5, parts.length); // Sanity check 393 final NetworkInfo.State state = parts[0].equals("null") 394 ? null : NetworkInfo.State.valueOf(parts[0]); 395 final NetworkInfo.DetailedState detailedState = parts[1].equals("null") 396 ? null : NetworkInfo.DetailedState.valueOf(parts[1]); 397 final boolean connected = Boolean.valueOf(parts[2]); 398 final String connectionCheckDetails = parts[3]; 399 final String networkInfo = parts[4]; 400 401 final StringBuilder errors = new StringBuilder(); 402 final NetworkInfo.State expectedState = NetworkInfo.State.CONNECTED; 403 final NetworkInfo.DetailedState expectedDetailedState = NetworkInfo.DetailedState.CONNECTED; 404 405 if (true != connected) { 406 errors.append(String.format("External site connection failed: expected %s, got %s\n", 407 true, connected)); 408 } 409 if (expectedState != state || expectedDetailedState != detailedState) { 410 errors.append(String.format("Connection state mismatch: expected %s/%s, got %s/%s\n", 411 expectedState, expectedDetailedState, state, detailedState)); 412 } 413 414 if (errors.length() > 0) { 415 errors.append("\tnetworkInfo: " + networkInfo + "\n"); 416 errors.append("\tconnectionCheckDetails: " + connectionCheckDetails + "\n"); 417 } 418 return errors.toString(); 419 } 420 421 private static void installAppAndAssertInstalled() throws Exception { 422 final CountDownLatch latch = new CountDownLatch(1); 423 final int[] result = {PackageInstaller.STATUS_SUCCESS}; 424 final BroadcastReceiver installStatusReceiver = new BroadcastReceiver() { 425 @Override 426 public void onReceive(Context context, Intent intent) { 427 final String pkgName = intent.getStringExtra(PackageInstaller.EXTRA_PACKAGE_NAME); 428 if (!TEST_PKG.equals(pkgName)) { 429 return; 430 } 431 result[0] = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, 432 PackageInstaller.STATUS_FAILURE); 433 latch.countDown(); 434 } 435 }; 436 mContext.registerReceiver(installStatusReceiver, new IntentFilter(ACTION_INSTALL_COMPLETE)); 437 try { 438 installApp(); 439 if (latch.await(WAIT_FOR_INSTALL_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { 440 if (result[0] != PackageInstaller.STATUS_SUCCESS) { 441 fail("Couldn't install test app, result: " 442 + valueToString(PackageInstaller.class, "STATUS_", result[0])); 443 } 444 } else { 445 fail("Timed out waiting for the test app to install"); 446 } 447 } finally { 448 mContext.unregisterReceiver(installStatusReceiver); 449 } 450 } 451 452 private static void installApp() throws Exception { 453 final Uri packageUri = Uri.parse(TEST_APP_URI); 454 final InputStream in = mContext.getContentResolver().openInputStream(packageUri); 455 456 final PackageInstaller packageInstaller 457 = mContext.getPackageManager().getPackageInstaller(); 458 final PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( 459 PackageInstaller.SessionParams.MODE_FULL_INSTALL); 460 params.setAppPackageName(TEST_PKG); 461 462 final int sessionId = packageInstaller.createSession(params); 463 final PackageInstaller.Session session = packageInstaller.openSession(sessionId); 464 465 OutputStream out = null; 466 try { 467 out = session.openWrite(TAG, 0, -1); 468 final byte[] buffer = new byte[65536]; 469 int c; 470 while ((c = in.read(buffer)) != -1) { 471 out.write(buffer, 0, c); 472 } 473 session.fsync(out); 474 } finally { 475 IoUtils.closeQuietly(in); 476 IoUtils.closeQuietly(out); 477 } 478 session.commit(createIntentSender(mContext, sessionId)); 479 } 480 481 private static IntentSender createIntentSender(Context context, int sessionId) { 482 PendingIntent pendingIntent = PendingIntent.getBroadcast( 483 context, sessionId, new Intent(ACTION_INSTALL_COMPLETE), 0); 484 return pendingIntent.getIntentSender(); 485 } 486}