1 package com.android.cts.verifier.jobscheduler; 2 3 import android.annotation.TargetApi; 4 import android.app.job.JobInfo; 5 import android.app.job.JobScheduler; 6 import android.content.BroadcastReceiver; 7 import android.content.Context; 8 import android.content.Intent; 9 import android.content.IntentFilter; 10 import android.os.AsyncTask; 11 import android.os.Bundle; 12 import android.os.BatteryManager; 13 import android.widget.Button; 14 import android.widget.ImageView; 15 import android.widget.TextView; 16 import android.view.View; 17 18 import com.android.cts.verifier.R; 19 import com.android.cts.verifier.TimerProgressBar; 20 21 /** 22 * This activity runs the following tests: 23 * - Ask the tester to ensure the phone is plugged in, and verify that jobs with charging 24 * constraints are run. 25 * - Ask the tester to unplug the phone, and verify that jobs with charging constraints will 26 * not run. 27 */ 28 @TargetApi(21) 29 public class ChargingConstraintTestActivity extends ConstraintTestActivity { 30 31 private static final int ON_CHARGING_JOB_ID = 32 ChargingConstraintTestActivity.class.hashCode() + 0; 33 private static final int OFF_CHARGING_JOB_ID = 34 ChargingConstraintTestActivity.class.hashCode() + 1; 35 36 // Time in milliseconds to wait after power is connected for the phone 37 // to get into charging mode. 38 private static final long WAIT_FOR_CHARGING_DURATION = 3 * 60 * 1000; 39 40 private static final int STATE_NOT_RUNNING = 0; 41 private static final int STATE_WAITING_TO_START_ON_CHARGING_TEST = 1; 42 private static final int STATE_ON_CHARGING_TEST_PASSED = 2; 43 44 private int mTestState; 45 TimerProgressBar mWaitingForChargingProgressBar; 46 TextView mWaitingForChargingTextView; 47 TextView mProblemWithChargerTextView; 48 49 @Override 50 protected void onCreate(Bundle savedInstanceState) { 51 super.onCreate(savedInstanceState); 52 53 // Set up the UI. 54 setContentView(R.layout.js_charging); 55 setPassFailButtonClickListeners(); 56 setInfoResources(R.string.js_charging_test, R.string.js_charging_instructions, -1); 57 mStartButton = (Button) findViewById(R.id.js_charging_start_test_button); 58 mWaitingForChargingProgressBar = (TimerProgressBar) findViewById( 59 R.id.js_waiting_for_charging_progress_bar); 60 mWaitingForChargingTextView = (TextView) findViewById( 61 R.id.js_waiting_for_charging_text_view); 62 mProblemWithChargerTextView = (TextView) findViewById( 63 R.id.js_problem_with_charger_text_view); 64 65 if (isDevicePluggedIn()){ 66 mStartButton.setEnabled(true); 67 } 68 69 if (!deviceHasBattery()) { 70 // This device has hardwired power, so do not prompt about connecting 71 // or disconnecting the charger, and ignore the "no power" test. 72 findViewById(R.id.charger_prompt).setVisibility(View.GONE); 73 findViewById(R.id.unplug_prompt).setVisibility(View.GONE); 74 findViewById(R.id.unplug_test_description).setVisibility(View.GONE); 75 } 76 77 hideWaitingForStableChargingViews(); 78 79 mTestState = STATE_NOT_RUNNING; 80 81 mJobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE); 82 83 // Register receiver for connected/disconnected power events. 84 IntentFilter intentFilter = new IntentFilter(); 85 intentFilter.addAction(Intent.ACTION_POWER_CONNECTED); 86 intentFilter.addAction(Intent.ACTION_POWER_DISCONNECTED); 87 intentFilter.addAction(BatteryManager.ACTION_CHARGING); 88 intentFilter.addAction(BatteryManager.ACTION_DISCHARGING); 89 90 registerReceiver(mChargingChangedReceiver, intentFilter); 91 } 92 93 @Override 94 protected void onDestroy() { 95 super.onDestroy(); 96 unregisterReceiver(mChargingChangedReceiver); 97 } 98 99 private boolean deviceHasBattery() { 100 final Intent batteryInfo = registerReceiver(null, 101 new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); 102 return batteryInfo.getBooleanExtra(BatteryManager.EXTRA_PRESENT, true); 103 } 104 105 private boolean isDevicePluggedIn() { 106 IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); 107 Intent batteryStatus = registerReceiver(null, ifilter); 108 int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1); 109 // 0 indicates device is on battery power 110 return status != 0; 111 } 112 113 @Override 114 public void startTestImpl() { 115 BatteryManager bm = (BatteryManager) getSystemService(BATTERY_SERVICE); 116 if (bm.isCharging()) { 117 new TestDevicePluggedInConstraint().execute(); 118 } else if (isDevicePluggedIn()) { 119 mTestState = STATE_WAITING_TO_START_ON_CHARGING_TEST; 120 showWaitingForStableChargingViews(); 121 } 122 } 123 124 private BroadcastReceiver mChargingChangedReceiver = new BroadcastReceiver() { 125 @Override 126 public void onReceive(Context context, Intent intent) { 127 if (BatteryManager.ACTION_CHARGING.equals(intent.getAction())) { 128 if (mTestState == STATE_WAITING_TO_START_ON_CHARGING_TEST) { 129 mWaitingForChargingProgressBar.forceComplete(); 130 hideWaitingForStableChargingViews(); 131 new TestDevicePluggedInConstraint().execute(); 132 } 133 } else if (BatteryManager.ACTION_DISCHARGING.equals(intent.getAction())) { 134 // ignore this [spurious!] broadcast on non-battery devices 135 if (deviceHasBattery()) { 136 if (mTestState == STATE_ON_CHARGING_TEST_PASSED) { 137 new TestDeviceUnpluggedConstraint().execute(); 138 } 139 } 140 } else if (Intent.ACTION_POWER_CONNECTED.equals(intent.getAction())) { 141 if (mTestState == STATE_WAITING_TO_START_ON_CHARGING_TEST) { 142 showWaitingForStableChargingViews(); 143 } else if (mTestState == STATE_NOT_RUNNING) { 144 mStartButton.setEnabled(true); 145 } 146 mStartButton.setEnabled(true); 147 } else if (Intent.ACTION_POWER_DISCONNECTED.equals(intent.getAction())) { 148 hideWaitingForStableChargingViews(); 149 } 150 } 151 }; 152 153 /** Simple state boolean we use to determine whether to continue with the second test. */ 154 private boolean mDeviceUnpluggedTestPassed = false; 155 156 /** 157 * Test blocks and can't be run on the main thread. 158 */ 159 private void testChargingConstraintFails_notCharging() { 160 mTestEnvironment.setUp(); 161 162 mTestEnvironment.setExpectedExecutions(0); 163 JobInfo runOnCharge = new JobInfo.Builder(OFF_CHARGING_JOB_ID, mMockComponent) 164 .setRequiresCharging(true) 165 .build(); 166 mJobScheduler.schedule(runOnCharge); 167 168 boolean testPassed; 169 try { 170 testPassed = mTestEnvironment.awaitTimeout(); 171 } catch (InterruptedException e) { 172 testPassed = false; 173 } 174 runOnUiThread(new ChargingConstraintTestResultRunner(OFF_CHARGING_JOB_ID, testPassed)); 175 } 176 177 /** 178 * Test blocks and can't be run on the main thread. 179 */ 180 private void testChargingConstraintExecutes_onCharging() { 181 mTestEnvironment.setUp(); 182 183 JobInfo delayConstraintAndUnexpiredDeadline = 184 new JobInfo.Builder(ON_CHARGING_JOB_ID, mMockComponent) 185 .setRequiresCharging(true) 186 .build(); 187 188 mTestEnvironment.setExpectedExecutions(1); 189 mJobScheduler.schedule(delayConstraintAndUnexpiredDeadline); 190 191 boolean testPassed; 192 try { 193 testPassed = mTestEnvironment.awaitExecution(); 194 } catch (InterruptedException e) { 195 testPassed = false; 196 } 197 198 mTestState = testPassed ? STATE_ON_CHARGING_TEST_PASSED : STATE_NOT_RUNNING; 199 runOnUiThread(new ChargingConstraintTestResultRunner(ON_CHARGING_JOB_ID, testPassed)); 200 } 201 202 /** Run test for when the <bold>device is not connected to power.</bold>. */ 203 private class TestDeviceUnpluggedConstraint extends AsyncTask<Void, Void, Void> { 204 @Override 205 protected Void doInBackground(Void... voids) { 206 testChargingConstraintFails_notCharging(); 207 notifyTestCompleted(); 208 return null; 209 } 210 211 @Override 212 protected void onPostExecute(Void res) { 213 mTestState = STATE_NOT_RUNNING; 214 mStartButton.setEnabled(true); 215 } 216 } 217 218 /** Run test for when the <bold>device is connected to power.</bold> */ 219 private class TestDevicePluggedInConstraint extends AsyncTask<Void, Void, Void> { 220 @Override 221 protected Void doInBackground(Void... voids) { 222 testChargingConstraintExecutes_onCharging(); 223 return null; 224 } 225 } 226 227 private class ChargingConstraintTestResultRunner extends TestResultRunner { 228 ChargingConstraintTestResultRunner(int jobId, boolean testPassed) { 229 super(jobId, testPassed); 230 } 231 232 @Override 233 public void run() { 234 ImageView view; 235 if (mJobId == OFF_CHARGING_JOB_ID) { 236 view = (ImageView) findViewById(R.id.charging_off_test_image); 237 } else if (mJobId == ON_CHARGING_JOB_ID) { 238 view = (ImageView) findViewById(R.id.charging_on_test_image); 239 } else { 240 noteInvalidTest(); 241 return; 242 } 243 view.setImageResource(mTestPassed ? R.drawable.fs_good : R.drawable.fs_error); 244 } 245 } 246 247 private void showWaitingForStableChargingViews() { 248 mWaitingForChargingProgressBar.start(WAIT_FOR_CHARGING_DURATION, 1000, 249 new TimerProgressBar.TimerExpiredCallback(){ 250 @Override 251 public void onTimerExpired() { 252 mProblemWithChargerTextView.setVisibility(View.VISIBLE); 253 } 254 } 255 ); 256 mWaitingForChargingProgressBar.setVisibility(View.VISIBLE); 257 mWaitingForChargingTextView.setVisibility(View.VISIBLE); 258 mProblemWithChargerTextView.setVisibility(View.GONE); 259 } 260 261 private void hideWaitingForStableChargingViews() { 262 mWaitingForChargingProgressBar.forceComplete(); 263 mWaitingForChargingProgressBar.setVisibility(View.GONE); 264 mWaitingForChargingTextView.setVisibility(View.GONE); 265 mProblemWithChargerTextView.setVisibility(View.GONE); 266 } 267 } 268