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.controls.cts; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertNotEquals; 21 import static org.junit.Assert.assertNotNull; 22 import static org.junit.Assert.assertNull; 23 import static org.mockito.Mockito.anyInt; 24 import static org.mockito.Mockito.mock; 25 import static org.mockito.Mockito.when; 26 27 import android.content.ComponentName; 28 import android.content.Context; 29 import android.content.Intent; 30 import android.content.res.Resources; 31 import android.service.controls.Control; 32 import android.service.controls.ControlsProviderService; 33 import android.service.controls.actions.BooleanAction; 34 import android.service.controls.actions.CommandAction; 35 import android.service.controls.actions.ControlAction; 36 import android.service.controls.actions.FloatAction; 37 import android.service.controls.actions.ModeAction; 38 import android.service.controls.templates.ControlTemplate; 39 import android.service.controls.templates.RangeTemplate; 40 import android.service.controls.templates.TemperatureControlTemplate; 41 import android.service.controls.templates.ToggleRangeTemplate; 42 import android.service.controls.templates.ToggleTemplate; 43 import android.test.mock.MockContext; 44 45 import androidx.test.runner.AndroidJUnit4; 46 47 import java.util.ArrayList; 48 import java.util.List; 49 import java.util.concurrent.Flow.Publisher; 50 import java.util.concurrent.Flow.Subscriber; 51 import java.util.concurrent.Flow.Subscription; 52 import java.util.function.Consumer; 53 54 import org.junit.Before; 55 import org.junit.Test; 56 import org.junit.runner.RunWith; 57 58 @RunWith(AndroidJUnit4.class) 59 public class CtsControlsServiceTest { 60 61 private static final String ACTION_ADD_CONTROL = "android.service.controls.action.ADD_CONTROL"; 62 private static final String EXTRA_CONTROL = "android.service.controls.extra.CONTROL"; 63 64 private CtsControlsService mControlsService; 65 66 @Before setUp()67 public void setUp() { 68 mControlsService = new CtsControlsService(); 69 } 70 71 @Test testLoadAllAvailable()72 public void testLoadAllAvailable() { 73 Publisher<Control> publisher = mControlsService.createPublisherForAllAvailable(); 74 List<Control> loadedControls = new ArrayList<>(); 75 subscribe(publisher, 10, loadedControls); 76 77 List<Control> expectedControls = new ArrayList<>(); 78 expectedControls.add(new Control.StatelessBuilder( 79 mControlsService.buildLight(false, 0.0f)).build()); 80 expectedControls.add(new Control.StatelessBuilder( 81 mControlsService.buildLock(false)).build()); 82 expectedControls.add(new Control.StatelessBuilder( 83 mControlsService.buildRoutine()).build()); 84 expectedControls.add(new Control.StatelessBuilder(mControlsService.buildThermostat( 85 TemperatureControlTemplate.MODE_OFF)).build()); 86 expectedControls.add(new Control.StatelessBuilder( 87 mControlsService.buildMower(false)).build()); 88 expectedControls.add(new Control.StatelessBuilder( 89 mControlsService.buildSwitch(false)).build()); 90 expectedControls.add(new Control.StatelessBuilder( 91 mControlsService.buildGate(false)).build()); 92 expectedControls.add(new Control.StatelessBuilder( 93 mControlsService.buildCamera(true)).build()); 94 95 assertControlsList(loadedControls, expectedControls); 96 } 97 98 @Test testLoadSuggested()99 public void testLoadSuggested() { 100 Publisher<Control> publisher = mControlsService.createPublisherForSuggested(); 101 List<Control> loadedControls = new ArrayList<>(); 102 subscribe(publisher, 3, loadedControls); 103 104 List<Control> expectedControls = new ArrayList<>(); 105 expectedControls.add(new Control.StatelessBuilder( 106 mControlsService.buildLight(false, 0.0f)).build()); 107 expectedControls.add(new Control.StatelessBuilder( 108 mControlsService.buildLock(false)).build()); 109 expectedControls.add(new Control.StatelessBuilder( 110 mControlsService.buildRoutine()).build()); 111 112 assertControlsList(loadedControls, expectedControls); 113 } 114 115 @Test testPublisherForSingleControl()116 public void testPublisherForSingleControl() { 117 List<String> idsToLoad = new ArrayList<>(); 118 idsToLoad.add("mower"); 119 120 Publisher<Control> publisher = mControlsService.createPublisherFor(idsToLoad); 121 List<Control> loadedControls = new ArrayList<>(); 122 subscribe(publisher, 10, loadedControls); 123 124 List<Control> expectedControls = new ArrayList<>(); 125 expectedControls.add(mControlsService.buildMower(false)); 126 127 assertControlsList(loadedControls, expectedControls); 128 } 129 130 @Test testPublisherForMultipleControls()131 public void testPublisherForMultipleControls() { 132 List<String> idsToLoad = new ArrayList<>(); 133 idsToLoad.add("lock"); 134 idsToLoad.add("light"); 135 136 Publisher<Control> publisher = mControlsService.createPublisherFor(idsToLoad); 137 List<Control> loadedControls = new ArrayList<>(); 138 subscribe(publisher, 10, loadedControls); 139 140 List<Control> expectedControls = new ArrayList<>(); 141 expectedControls.add(mControlsService.buildLock(false)); 142 expectedControls.add(mControlsService.buildLight(false, 0.0f)); 143 144 assertControlsList(loadedControls, expectedControls); 145 } 146 147 @Test testBooleanAction()148 public void testBooleanAction() { 149 List<String> idsToLoad = new ArrayList<>(); 150 idsToLoad.add("switch"); 151 152 Publisher<Control> publisher = mControlsService.createPublisherFor(idsToLoad); 153 List<Control> loadedControls = new ArrayList<>(); 154 subscribe(publisher, 10, loadedControls); 155 156 BooleanAction action = new BooleanAction("action", true); 157 assertEquals(action.getActionType(), ControlAction.TYPE_BOOLEAN); 158 mControlsService.performControlAction("switch", action, 159 assertConsumer(ControlAction.RESPONSE_OK)); 160 161 List<Control> expectedControls = new ArrayList<>(); 162 expectedControls.add(mControlsService.buildSwitch(false)); 163 expectedControls.add(mControlsService.buildSwitch(true)); 164 165 assertControlsList(loadedControls, expectedControls); 166 } 167 168 @Test testFloatAction()169 public void testFloatAction() { 170 List<String> idsToLoad = new ArrayList<>(); 171 idsToLoad.add("light"); 172 173 Publisher<Control> publisher = mControlsService.createPublisherFor(idsToLoad); 174 List<Control> loadedControls = new ArrayList<>(); 175 subscribe(publisher, 10, loadedControls); 176 177 mControlsService.performControlAction("light", new BooleanAction("action", true), 178 assertConsumer(ControlAction.RESPONSE_OK)); 179 180 FloatAction action = new FloatAction("action", 80.0f); 181 assertEquals(action.getActionType(), ControlAction.TYPE_FLOAT); 182 mControlsService.performControlAction("light", action, 183 assertConsumer(ControlAction.RESPONSE_OK)); 184 185 List<Control> expectedControls = new ArrayList<>(); 186 expectedControls.add(mControlsService.buildLight(false, 0.0f)); 187 expectedControls.add(mControlsService.buildLight(true, 50.0f)); 188 expectedControls.add(mControlsService.buildLight(true, 80.0f)); 189 190 assertControlsList(loadedControls, expectedControls); 191 } 192 193 @Test testCommandAction()194 public void testCommandAction() { 195 List<String> idsToLoad = new ArrayList<>(); 196 idsToLoad.add("routine"); 197 198 Publisher<Control> publisher = mControlsService.createPublisherFor(idsToLoad); 199 List<Control> loadedControls = new ArrayList<>(); 200 subscribe(publisher, 10, loadedControls); 201 202 CommandAction action = new CommandAction("action"); 203 assertEquals(action.getActionType(), ControlAction.TYPE_COMMAND); 204 mControlsService.performControlAction("routine", action, 205 assertConsumer(ControlAction.RESPONSE_OK)); 206 207 List<Control> expectedControls = new ArrayList<>(); 208 expectedControls.add(mControlsService.buildRoutine()); 209 expectedControls.add(mControlsService.buildRoutine()); 210 211 assertControlsList(loadedControls, expectedControls); 212 } 213 214 @Test testBooleanActionWithPinChallenge()215 public void testBooleanActionWithPinChallenge() { 216 List<String> idsToLoad = new ArrayList<>(); 217 idsToLoad.add("lock"); 218 219 Publisher<Control> publisher = mControlsService.createPublisherFor(idsToLoad); 220 List<Control> loadedControls = new ArrayList<>(); 221 subscribe(publisher, 10, loadedControls); 222 223 mControlsService.performControlAction("lock", new BooleanAction("action", true), 224 assertConsumer(ControlAction.RESPONSE_CHALLENGE_PIN)); 225 226 mControlsService.performControlAction("lock", new BooleanAction("action", true, "1234"), 227 assertConsumer(ControlAction.RESPONSE_OK)); 228 229 List<Control> expectedControls = new ArrayList<>(); 230 expectedControls.add(mControlsService.buildLock(false)); 231 expectedControls.add(mControlsService.buildLock(true)); 232 233 assertControlsList(loadedControls, expectedControls); 234 } 235 236 @Test testBooleanActionWithPassphraseChallenge()237 public void testBooleanActionWithPassphraseChallenge() { 238 List<String> idsToLoad = new ArrayList<>(); 239 idsToLoad.add("gate"); 240 241 Publisher<Control> publisher = mControlsService.createPublisherFor(idsToLoad); 242 List<Control> loadedControls = new ArrayList<>(); 243 subscribe(publisher, 10, loadedControls); 244 245 mControlsService.performControlAction("gate", new BooleanAction("action", true), 246 assertConsumer(ControlAction.RESPONSE_CHALLENGE_PASSPHRASE)); 247 248 mControlsService.performControlAction("gate", new BooleanAction("action", true, "abc123"), 249 assertConsumer(ControlAction.RESPONSE_OK)); 250 251 List<Control> expectedControls = new ArrayList<>(); 252 expectedControls.add(mControlsService.buildGate(false)); 253 expectedControls.add(mControlsService.buildGate(true)); 254 255 assertControlsList(loadedControls, expectedControls); 256 } 257 258 @Test testBooleanActionWithAckChallenge()259 public void testBooleanActionWithAckChallenge() { 260 List<String> idsToLoad = new ArrayList<>(); 261 idsToLoad.add("mower"); 262 263 Publisher<Control> publisher = mControlsService.createPublisherFor(idsToLoad); 264 List<Control> loadedControls = new ArrayList<>(); 265 subscribe(publisher, 10, loadedControls); 266 267 mControlsService.performControlAction("mower", new BooleanAction("action", true), 268 assertConsumer(ControlAction.RESPONSE_CHALLENGE_ACK)); 269 270 mControlsService.performControlAction("mower", new BooleanAction("action", true, "true"), 271 assertConsumer(ControlAction.RESPONSE_OK)); 272 273 List<Control> expectedControls = new ArrayList<>(); 274 expectedControls.add(mControlsService.buildMower(false)); 275 expectedControls.add(mControlsService.buildMower(true)); 276 277 assertControlsList(loadedControls, expectedControls); 278 } 279 280 @Test testModeAction()281 public void testModeAction() { 282 List<String> idsToLoad = new ArrayList<>(); 283 idsToLoad.add("thermostat"); 284 285 Publisher<Control> publisher = mControlsService.createPublisherFor(idsToLoad); 286 List<Control> loadedControls = new ArrayList<>(); 287 subscribe(publisher, 10, loadedControls); 288 289 ModeAction action = new ModeAction("action", TemperatureControlTemplate.MODE_COOL); 290 assertEquals(action.getActionType(), ControlAction.TYPE_MODE); 291 mControlsService.performControlAction("thermostat", action, 292 assertConsumer(ControlAction.RESPONSE_OK)); 293 294 List<Control> expectedControls = new ArrayList<>(); 295 expectedControls.add(mControlsService.buildThermostat(TemperatureControlTemplate.MODE_OFF)); 296 expectedControls.add(mControlsService.buildThermostat( 297 TemperatureControlTemplate.MODE_COOL)); 298 299 assertControlsList(loadedControls, expectedControls); 300 } 301 302 @Test testRequestAddControl()303 public void testRequestAddControl() { 304 Resources res = mock(Resources.class); 305 when(res.getString(anyInt())).thenReturn(""); 306 307 final ComponentName testComponent = new ComponentName("TestPkg", "TestClass"); 308 final Control control = new Control.StatelessBuilder(mControlsService.buildMower(false)) 309 .build(); 310 311 Context context = new MockContext() { 312 public Resources getResources() { 313 return res; 314 } 315 316 public void sendBroadcast(Intent intent, String receiverPermission) { 317 assertEquals(intent.getAction(), ACTION_ADD_CONTROL); 318 assertEquals((ComponentName) intent.getParcelableExtra(Intent.EXTRA_COMPONENT_NAME), 319 testComponent); 320 assertEquals((Control) intent.getParcelableExtra(EXTRA_CONTROL), control); 321 assertEquals(receiverPermission, "android.permission.BIND_CONTROLS"); 322 } 323 }; 324 325 ControlsProviderService.requestAddControl(context, testComponent, control); 326 } 327 assertConsumer(int expectedStatus)328 private Consumer<Integer> assertConsumer(int expectedStatus) { 329 return (status) -> { 330 ControlAction.isValidResponse(status); 331 assertEquals((int) status, expectedStatus); 332 }; 333 } 334 subscribe(Publisher<Control> publisher, final int request, final List<Control> addToList)335 private void subscribe(Publisher<Control> publisher, final int request, 336 final List<Control> addToList) { 337 publisher.subscribe(new Subscriber<Control>() { 338 public void onSubscribe(Subscription s) { 339 s.request(request); 340 } 341 342 public void onNext(Control c) { 343 addToList.add(c); 344 } 345 346 public void onError(Throwable t) { 347 throw new IllegalStateException("onError should not be called here"); 348 } 349 350 public void onComplete() { 351 352 } 353 }); 354 } 355 assertControlsList(List<Control> actualControls, List<Control> expectedControls)356 private void assertControlsList(List<Control> actualControls, List<Control> expectedControls) { 357 assertEquals(actualControls.size(), expectedControls.size()); 358 359 for (int i = 0; i < actualControls.size(); i++) { 360 assertControlEquals(actualControls.get(i), expectedControls.get(i)); 361 } 362 } 363 assertControlEquals(Control c1, Control c2)364 private void assertControlEquals(Control c1, Control c2) { 365 assertEquals(c1.getTitle(), c2.getTitle()); 366 assertEquals(c1.getSubtitle(), c2.getSubtitle()); 367 assertEquals(c1.getStructure(), c2.getStructure()); 368 assertEquals(c1.getZone(), c2.getZone()); 369 assertEquals(c1.getDeviceType(), c2.getDeviceType()); 370 assertEquals(c1.getStatus(), c2.getStatus()); 371 assertEquals(c1.getControlId(), c2.getControlId()); 372 assertEquals(c1.getCustomIcon(), c2.getCustomIcon()); 373 assertEquals(c1.getCustomColor(), c2.getCustomColor()); 374 375 assertTemplateEquals(c1.getControlTemplate(), c2.getControlTemplate()); 376 } 377 assertTemplateEquals(ControlTemplate ct1, ControlTemplate ct2)378 private void assertTemplateEquals(ControlTemplate ct1, ControlTemplate ct2) { 379 if (ct1 == null) { 380 assertNull(ct2); 381 return; 382 } else { 383 assertNotNull(ct2); 384 } 385 386 assertNotEquals(ct1, ControlTemplate.getErrorTemplate()); 387 assertNotEquals(ct2, ControlTemplate.getErrorTemplate()); 388 assertEquals(ct1.getTemplateType(), ct2.getTemplateType()); 389 assertEquals(ct1.getTemplateId(), ct2.getTemplateId()); 390 391 switch (ct1.getTemplateType()) { 392 case ControlTemplate.TYPE_TOGGLE: 393 assertToggleTemplate((ToggleTemplate) ct1, (ToggleTemplate) ct2); 394 break; 395 case ControlTemplate.TYPE_RANGE: 396 assertRangeTemplate((RangeTemplate) ct1, (RangeTemplate) ct2); 397 break; 398 case ControlTemplate.TYPE_TEMPERATURE: 399 assertTemperatureControlTemplate((TemperatureControlTemplate) ct1, 400 (TemperatureControlTemplate) ct2); 401 break; 402 case ControlTemplate.TYPE_TOGGLE_RANGE: 403 assertToggleRangeTemplate((ToggleRangeTemplate) ct1, (ToggleRangeTemplate) ct2); 404 break; 405 } 406 } 407 assertToggleTemplate(ToggleTemplate t1, ToggleTemplate t2)408 private void assertToggleTemplate(ToggleTemplate t1, ToggleTemplate t2) { 409 assertEquals(t1.isChecked(), t2.isChecked()); 410 assertEquals(t1.getContentDescription(), t2.getContentDescription()); 411 } 412 assertRangeTemplate(RangeTemplate t1, RangeTemplate t2)413 private void assertRangeTemplate(RangeTemplate t1, RangeTemplate t2) { 414 assertEquals(t1.getMinValue(), t2.getMinValue(), 0.0f); 415 assertEquals(t1.getMaxValue(), t2.getMaxValue(), 0.0f); 416 assertEquals(t1.getCurrentValue(), t2.getCurrentValue(), 0.0f); 417 assertEquals(t1.getStepValue(), t2.getStepValue(), 0.0f); 418 assertEquals(t1.getFormatString(), t2.getFormatString()); 419 } 420 assertTemperatureControlTemplate(TemperatureControlTemplate t1, TemperatureControlTemplate t2)421 private void assertTemperatureControlTemplate(TemperatureControlTemplate t1, 422 TemperatureControlTemplate t2) { 423 assertEquals(t1.getCurrentMode(), t2.getCurrentMode()); 424 assertEquals(t1.getCurrentActiveMode(), t2.getCurrentActiveMode()); 425 assertEquals(t1.getModes(), t2.getModes()); 426 assertTemplateEquals(t1.getTemplate(), t2.getTemplate()); 427 } 428 assertToggleRangeTemplate(ToggleRangeTemplate t1, ToggleRangeTemplate t2)429 private void assertToggleRangeTemplate(ToggleRangeTemplate t1, ToggleRangeTemplate t2) { 430 assertEquals(t1.isChecked(), t2.isChecked()); 431 assertEquals(t1.getActionDescription(), t2.getActionDescription()); 432 assertRangeTemplate(t1.getRange(), t2.getRange()); 433 } 434 } 435