1 /* 2 * Copyright (C) 2020 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 17 package android.location.cts.common; 18 19 import android.content.Context; 20 import android.content.pm.PackageManager; 21 import android.net.ConnectivityManager; 22 import android.net.NetworkInfo; 23 import android.util.Log; 24 25 import com.android.compatibility.common.util.SystemUtil; 26 27 import java.util.concurrent.Callable; 28 import java.util.concurrent.CountDownLatch; 29 import java.util.concurrent.TimeUnit; 30 31 public class TestUtils { 32 private static final String TAG = "LocationTestUtils"; 33 34 private static final long STANDARD_WAIT_TIME_MS = 50; 35 private static final long STANDARD_SLEEP_TIME_MS = 50; 36 37 private static final int DATA_CONNECTION_CHECK_INTERVAL_MS = 500; 38 private static final int DATA_CONNECTION_CHECK_COUNT = 10; // 500 * 10 - Roughly 5 secs wait 39 waitFor(CountDownLatch latch, int timeInSec)40 public static boolean waitFor(CountDownLatch latch, int timeInSec) throws InterruptedException { 41 // Since late 2014, if the main thread has been occupied for long enough, Android will 42 // increase its priority. Such new behavior can causes starvation to the background thread - 43 // even if the main thread has called await() to yield its execution, the background thread 44 // still can't get scheduled. 45 // 46 // Here we're trying to wait on the main thread for a PendingIntent from a background 47 // thread. Because of the starvation problem, the background thread may take up to 5 minutes 48 // to deliver the PendingIntent if we simply call await() on the main thread. In order to 49 // give the background thread a chance to run, we call Thread.sleep() in a loop. Such dirty 50 // hack isn't ideal, but at least it can work. 51 // 52 // See also: b/17423027 53 long waitTimeRounds = (TimeUnit.SECONDS.toMillis(timeInSec)) / 54 (STANDARD_WAIT_TIME_MS + STANDARD_SLEEP_TIME_MS); 55 for (int i = 0; i < waitTimeRounds; ++i) { 56 Thread.sleep(STANDARD_SLEEP_TIME_MS); 57 if (latch.await(STANDARD_WAIT_TIME_MS, TimeUnit.MILLISECONDS)) { 58 return true; 59 } 60 } 61 return false; 62 } 63 waitForWithCondition(int timeInSec, Callable<Boolean> callback)64 public static boolean waitForWithCondition(int timeInSec, Callable<Boolean> callback) 65 throws Exception { 66 long waitTimeRounds = (TimeUnit.SECONDS.toMillis(timeInSec)) / STANDARD_SLEEP_TIME_MS; 67 for (int i = 0; i < waitTimeRounds; ++i) { 68 Thread.sleep(STANDARD_SLEEP_TIME_MS); 69 if(callback.call()) return true; 70 } 71 return false; 72 } 73 deviceHasGpsFeature(Context context)74 public static boolean deviceHasGpsFeature(Context context) { 75 // If device does not have a GPS, skip the test. 76 if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LOCATION_GPS)) { 77 return true; 78 } 79 Log.w(TAG, "GPS feature not present on device, skipping GPS test."); 80 return false; 81 } 82 83 /** 84 * Returns whether the device is currently connected to a wifi or cellular. 85 * 86 * @param context {@link Context} object 87 * @return {@code true} if connected to Wifi or Cellular; {@code false} otherwise 88 */ isConnectedToWifiOrCellular(Context context)89 public static boolean isConnectedToWifiOrCellular(Context context) { 90 NetworkInfo info = getActiveNetworkInfo(context); 91 return info != null 92 && info.isConnected() 93 && (info.getType() == ConnectivityManager.TYPE_WIFI 94 || info.getType() == ConnectivityManager.TYPE_MOBILE); 95 } 96 97 /** 98 * Gets the active network info. 99 * 100 * @param context {@link Context} object 101 * @return {@link NetworkInfo} 102 */ getActiveNetworkInfo(Context context)103 private static NetworkInfo getActiveNetworkInfo(Context context) { 104 ConnectivityManager cm = getConnectivityManager(context); 105 if (cm != null) { 106 return cm.getActiveNetworkInfo(); 107 } 108 return null; 109 } 110 111 /** 112 * Gets the connectivity manager. 113 * 114 * @param context {@link Context} object 115 * @return {@link ConnectivityManager} 116 */ getConnectivityManager(Context context)117 public static ConnectivityManager getConnectivityManager(Context context) { 118 return (ConnectivityManager) context.getApplicationContext() 119 .getSystemService(Context.CONNECTIVITY_SERVICE); 120 } 121 122 /** 123 * Returns {@code true} if the setting {@code airplane_mode_on} is set to 1. 124 */ isAirplaneModeOn()125 public static boolean isAirplaneModeOn() { 126 return SystemUtil.runShellCommand("settings get global airplane_mode_on") 127 .trim().equals("1"); 128 } 129 130 /** 131 * Changes the setting {@code airplane_mode_on} to 1 if {@code enableAirplaneMode} 132 * is {@code true}. Otherwise, it is set to 0. 133 * 134 * <p>Waits for a certain time duration for network connections to turn on/off based on 135 * {@code enableAirplaneMode}. 136 */ setAirplaneModeOn(Context context, boolean enableAirplaneMode)137 public static void setAirplaneModeOn(Context context, 138 boolean enableAirplaneMode) throws InterruptedException { 139 Log.i(TAG, "Setting airplane_mode_on to " + enableAirplaneMode); 140 SystemUtil.runShellCommand("cmd connectivity airplane-mode " 141 + (enableAirplaneMode ? "enable" : "disable")); 142 143 // Wait for a few seconds until the airplane mode changes take effect. The airplane mode on 144 // state and the WiFi/cell connected state are opposite. So, we wait while they are the 145 // same or until the specified time interval expires. 146 // 147 // Note that in unusual cases where the WiFi/cell are not in a connected state before 148 // turning on airplane mode, then turning off airplane mode won't restore either of 149 // these connections, and then the wait time below will be wasteful. 150 int dataConnectionCheckCount = DATA_CONNECTION_CHECK_COUNT; 151 while (enableAirplaneMode == isConnectedToWifiOrCellular(context)) { 152 if (--dataConnectionCheckCount <= 0) { 153 Log.w(TAG, "Airplane mode " + (enableAirplaneMode ? "on" : "off") 154 + " setting did not take effect on WiFi/cell connected state."); 155 return; 156 } 157 Thread.sleep(DATA_CONNECTION_CHECK_INTERVAL_MS); 158 } 159 } 160 } 161