1 /* 2 * Copyright (C) 2008 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.app.cts; 18 19 import android.app.ActivityManager; 20 import android.app.Notification; 21 import android.app.NotificationChannel; 22 import android.app.NotificationManager; 23 import android.app.PendingIntent; 24 import android.app.stubs.ActivityTestsBase; 25 import android.app.stubs.LocalDeniedService; 26 import android.app.stubs.LocalForegroundService; 27 import android.app.stubs.LocalGrantedService; 28 import android.app.stubs.LocalService; 29 import android.content.ComponentName; 30 import android.content.Context; 31 import android.content.Intent; 32 import android.content.ServiceConnection; 33 import android.support.test.InstrumentationRegistry; 34 import android.os.Binder; 35 import android.os.Bundle; 36 import android.os.IBinder; 37 import android.os.Parcel; 38 import android.os.RemoteException; 39 import android.service.notification.StatusBarNotification; 40 import android.test.suitebuilder.annotation.MediumTest; 41 import android.util.Log; 42 import android.app.stubs.R; 43 44 import com.android.compatibility.common.util.IBinderParcelable; 45 import com.android.compatibility.common.util.SystemUtil; 46 47 import java.util.List; 48 49 public class ServiceTest extends ActivityTestsBase { 50 private static final String TAG = "ServiceTest"; 51 private static final String NOTIFICATION_CHANNEL_ID = TAG; 52 private static final int STATE_START_1 = 0; 53 private static final int STATE_START_2 = 1; 54 private static final int STATE_START_3 = 2; 55 private static final int STATE_UNBIND = 3; 56 private static final int STATE_DESTROY = 4; 57 private static final int STATE_REBIND = 5; 58 private static final int STATE_UNBIND_ONLY = 6; 59 private static final int DELAY = 5000; 60 private static final 61 String EXIST_CONN_TO_RECEIVE_SERVICE = "existing connection to receive service"; 62 private static final String EXIST_CONN_TO_LOSE_SERVICE = "existing connection to lose service"; 63 private static final String EXTERNAL_SERVICE_PACKAGE = "com.android.app2"; 64 private static final String EXTERNAL_SERVICE_COMPONENT = 65 EXTERNAL_SERVICE_PACKAGE + "/android.app.stubs.LocalService"; 66 private int mExpectedServiceState; 67 private Context mContext; 68 private Intent mLocalService; 69 private Intent mLocalDeniedService; 70 private Intent mLocalForegroundService; 71 private Intent mLocalGrantedService; 72 private Intent mLocalService_ApplicationHasPermission; 73 private Intent mLocalService_ApplicationDoesNotHavePermission; 74 private Intent mExternalService; 75 76 private IBinder mStateReceiver; 77 78 private static class EmptyConnection implements ServiceConnection { 79 @Override 80 public void onServiceConnected(ComponentName name, IBinder service) { 81 } 82 83 @Override 84 public void onServiceDisconnected(ComponentName name) { 85 } 86 } 87 88 private class TestConnection implements ServiceConnection { 89 private final boolean mExpectDisconnect; 90 private final boolean mSetReporter; 91 private boolean mMonitor; 92 private int mCount; 93 94 public TestConnection(boolean expectDisconnect, boolean setReporter) { 95 mExpectDisconnect = expectDisconnect; 96 mSetReporter = setReporter; 97 mMonitor = !setReporter; 98 } 99 100 void setMonitor(boolean v) { 101 mMonitor = v; 102 } 103 104 @Override 105 public void onServiceConnected(ComponentName name, IBinder service) { 106 if (mSetReporter) { 107 Parcel data = Parcel.obtain(); 108 data.writeInterfaceToken(LocalService.SERVICE_LOCAL); 109 data.writeStrongBinder(mStateReceiver); 110 try { 111 service.transact(LocalService.SET_REPORTER_CODE, data, null, 0); 112 } catch (RemoteException e) { 113 finishBad("DeadObjectException when sending reporting object"); 114 } 115 data.recycle(); 116 } 117 118 if (mMonitor) { 119 mCount++; 120 if (mExpectedServiceState == STATE_START_1) { 121 if (mCount == 1) { 122 finishGood(); 123 } else { 124 finishBad("onServiceConnected() again on an object when it " 125 + "should have been the first time"); 126 } 127 } else if (mExpectedServiceState == STATE_START_2) { 128 if (mCount == 2) { 129 finishGood(); 130 } else { 131 finishBad("onServiceConnected() the first time on an object " 132 + "when it should have been the second time"); 133 } 134 } else { 135 finishBad("onServiceConnected() called unexpectedly"); 136 } 137 } 138 } 139 140 @Override 141 public void onServiceDisconnected(ComponentName name) { 142 if (mMonitor) { 143 if (mExpectedServiceState == STATE_DESTROY) { 144 if (mExpectDisconnect) { 145 finishGood(); 146 } else { 147 finishBad("onServiceDisconnected() when it shouldn't have been"); 148 } 149 } else { 150 finishBad("onServiceDisconnected() called unexpectedly"); 151 } 152 } 153 } 154 } 155 156 private void startExpectResult(Intent service) { 157 startExpectResult(service, new Bundle()); 158 } 159 160 private void startExpectResult(Intent service, Bundle bundle) { 161 bundle.putParcelable(LocalService.REPORT_OBJ_NAME, new IBinderParcelable(mStateReceiver)); 162 163 boolean success = false; 164 try { 165 mExpectedServiceState = STATE_START_1; 166 mContext.startService(new Intent(service).putExtras(bundle)); 167 waitForResultOrThrow(DELAY, "service to start first time"); 168 mExpectedServiceState = STATE_START_2; 169 mContext.startService(new Intent(service).putExtras(bundle)); 170 waitForResultOrThrow(DELAY, "service to start second time"); 171 success = true; 172 } finally { 173 if (!success) { 174 mContext.stopService(service); 175 } 176 } 177 mExpectedServiceState = STATE_DESTROY; 178 mContext.stopService(service); 179 waitForResultOrThrow(DELAY, "service to be destroyed"); 180 } 181 182 private NotificationManager getNotificationManager() { 183 NotificationManager notificationManager = 184 (NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE); 185 return notificationManager; 186 } 187 188 private void sendNotification(int id, String title) { 189 Notification notification = new Notification.Builder(getContext(), NOTIFICATION_CHANNEL_ID) 190 .setContentTitle(title) 191 .setSmallIcon(R.drawable.black) 192 .build(); 193 getNotificationManager().notify(id, notification); 194 } 195 196 private void cancelNotification(int id) { 197 getNotificationManager().cancel(id); 198 } 199 200 private void assertNotification(int id, String expectedTitle) { 201 String packageName = getContext().getPackageName(); 202 String errorMessage = null; 203 for (int i = 1; i<=2; i++) { 204 errorMessage = null; 205 StatusBarNotification[] sbns = getNotificationManager().getActiveNotifications(); 206 for (StatusBarNotification sbn : sbns) { 207 if (sbn.getId() == id && sbn.getPackageName().equals(packageName)) { 208 String actualTitle = 209 sbn.getNotification().extras.getString(Notification.EXTRA_TITLE); 210 if (expectedTitle.equals(actualTitle)) { 211 return; 212 } 213 // It's possible the notification hasn't been updated yet, so save the error 214 // message to only fail after retrying. 215 errorMessage = String.format("Wrong title for notification #%d: " 216 + "expected '%s', actual '%s'", id, expectedTitle, actualTitle); 217 Log.w(TAG, errorMessage); 218 } 219 } 220 // Notification might not be rendered yet, wait and try again... 221 try { 222 Thread.sleep(DELAY); 223 } catch (InterruptedException e) { 224 Thread.currentThread().interrupt(); 225 } 226 } 227 if (errorMessage != null) { 228 fail(errorMessage); 229 } 230 fail("No notification with id " + id + " for package " + packageName); 231 } 232 233 private void assertNoNotification(int id) { 234 String packageName = getContext().getPackageName(); 235 StatusBarNotification found = null; 236 for (int i = 1; i<=2; i++) { 237 found = null; 238 StatusBarNotification[] sbns = getNotificationManager().getActiveNotifications(); 239 for (StatusBarNotification sbn : sbns) { 240 if (sbn.getId() == id && sbn.getPackageName().equals(packageName)) { 241 found = sbn; 242 break; 243 } 244 } 245 if (found != null) { 246 // Notification might not be canceled yet, wait and try again... 247 try { 248 Thread.sleep(DELAY); 249 } catch (InterruptedException e) { 250 Thread.currentThread().interrupt(); 251 } 252 } 253 } 254 assertNull("Found notification with id " + id + " for package " + packageName + ": " 255 + found, found); 256 } 257 258 /** 259 * test the service lifecycle, a service can be used in two ways: 260 * 1 It can be started and allowed to run until someone stops it or it stops itself. 261 * In this mode, it's started by calling Context.startService() 262 * and stopped by calling Context.stopService(). 263 * It can stop itself by calling Service.stopSelf() or Service.stopSelfResult(). 264 * Only one stopService() call is needed to stop the service, 265 * no matter how many times startService() was called. 266 * 2 It can be operated programmatically using an interface that it defines and exports. 267 * Clients establish a connection to the Service object 268 * and use that connection to call into the service. 269 * The connection is established by calling Context.bindService(), 270 * and is closed by calling Context.unbindService(). 271 * Multiple clients can bind to the same service. 272 * If the service has not already been launched, bindService() can optionally launch it. 273 */ 274 private void bindExpectResult(Intent service) { 275 TestConnection conn = new TestConnection(true, false); 276 TestConnection conn2 = new TestConnection(false, false); 277 boolean success = false; 278 try { 279 // Expect to see the TestConnection connected. 280 mExpectedServiceState = STATE_START_1; 281 mContext.bindService(service, conn, 0); 282 mContext.startService(service); 283 waitForResultOrThrow(DELAY, EXIST_CONN_TO_RECEIVE_SERVICE); 284 285 // Expect to see the second TestConnection connected. 286 mContext.bindService(service, conn2, 0); 287 waitForResultOrThrow(DELAY, "new connection to receive service"); 288 289 mContext.unbindService(conn2); 290 success = true; 291 } finally { 292 if (!success) { 293 mContext.unbindService(conn); 294 mContext.unbindService(conn2); 295 mContext.stopService(service); 296 } 297 } 298 299 // Expect to see the TestConnection disconnected. 300 mExpectedServiceState = STATE_DESTROY; 301 mContext.stopService(service); 302 waitForResultOrThrow(DELAY, EXIST_CONN_TO_LOSE_SERVICE); 303 304 mContext.unbindService(conn); 305 306 conn = new TestConnection(true, true); 307 success = false; 308 try { 309 // Expect to see the TestConnection connected. 310 conn.setMonitor(true); 311 mExpectedServiceState = STATE_START_1; 312 mContext.bindService(service, conn, 0); 313 mContext.startService(service); 314 waitForResultOrThrow(DELAY, EXIST_CONN_TO_RECEIVE_SERVICE); 315 316 success = true; 317 } finally { 318 if (!success) { 319 mContext.unbindService(conn); 320 mContext.stopService(service); 321 } 322 } 323 324 // Expect to see the service unbind and then destroyed. 325 conn.setMonitor(false); 326 mExpectedServiceState = STATE_UNBIND; 327 mContext.stopService(service); 328 waitForResultOrThrow(DELAY, EXIST_CONN_TO_LOSE_SERVICE); 329 330 mContext.unbindService(conn); 331 332 conn = new TestConnection(true, true); 333 success = false; 334 try { 335 // Expect to see the TestConnection connected. 336 conn.setMonitor(true); 337 mExpectedServiceState = STATE_START_1; 338 mContext.bindService(service, conn, 0); 339 mContext.startService(service); 340 waitForResultOrThrow(DELAY, EXIST_CONN_TO_RECEIVE_SERVICE); 341 342 success = true; 343 } finally { 344 if (!success) { 345 mContext.unbindService(conn); 346 mContext.stopService(service); 347 } 348 } 349 350 // Expect to see the service unbind but not destroyed. 351 conn.setMonitor(false); 352 mExpectedServiceState = STATE_UNBIND_ONLY; 353 mContext.unbindService(conn); 354 waitForResultOrThrow(DELAY, "existing connection to unbind service"); 355 356 // Expect to see the service rebound. 357 mExpectedServiceState = STATE_REBIND; 358 mContext.bindService(service, conn, 0); 359 waitForResultOrThrow(DELAY, "existing connection to rebind service"); 360 361 // Expect to see the service unbind and then destroyed. 362 mExpectedServiceState = STATE_UNBIND; 363 mContext.stopService(service); 364 waitForResultOrThrow(DELAY, EXIST_CONN_TO_LOSE_SERVICE); 365 366 mContext.unbindService(conn); 367 } 368 369 /** 370 * test automatically create the service as long as the binding exists 371 * and disconnect from an application service 372 */ 373 private void bindAutoExpectResult(Intent service) { 374 TestConnection conn = new TestConnection(false, true); 375 boolean success = false; 376 try { 377 conn.setMonitor(true); 378 mExpectedServiceState = STATE_START_1; 379 mContext.bindService( 380 service, conn, Context.BIND_AUTO_CREATE); 381 waitForResultOrThrow(DELAY, "connection to start and receive service"); 382 success = true; 383 } finally { 384 if (!success) { 385 mContext.unbindService(conn); 386 } 387 } 388 mExpectedServiceState = STATE_UNBIND; 389 mContext.unbindService(conn); 390 waitForResultOrThrow(DELAY, "disconnecting from service"); 391 } 392 393 @Override 394 protected void setUp() throws Exception { 395 super.setUp(); 396 mContext = getContext(); 397 mLocalService = new Intent(mContext, LocalService.class); 398 mExternalService = new Intent(); 399 mExternalService.setComponent(ComponentName.unflattenFromString(EXTERNAL_SERVICE_COMPONENT)); 400 mLocalForegroundService = new Intent(mContext, LocalForegroundService.class); 401 mLocalDeniedService = new Intent(mContext, LocalDeniedService.class); 402 mLocalGrantedService = new Intent(mContext, LocalGrantedService.class); 403 mLocalService_ApplicationHasPermission = new Intent( 404 LocalService.SERVICE_LOCAL_GRANTED, null /*uri*/, mContext, LocalService.class); 405 mLocalService_ApplicationDoesNotHavePermission = new Intent( 406 LocalService.SERVICE_LOCAL_DENIED, null /*uri*/, mContext, LocalService.class); 407 mStateReceiver = new MockBinder(); 408 getNotificationManager().createNotificationChannel(new NotificationChannel( 409 NOTIFICATION_CHANNEL_ID, "name", NotificationManager.IMPORTANCE_DEFAULT)); 410 } 411 412 @Override 413 protected void tearDown() throws Exception { 414 super.tearDown(); 415 getNotificationManager().deleteNotificationChannel(NOTIFICATION_CHANNEL_ID); 416 mContext.stopService(mLocalService); 417 mContext.stopService(mLocalForegroundService); 418 mContext.stopService(mLocalGrantedService); 419 mContext.stopService(mLocalService_ApplicationHasPermission); 420 mContext.stopService(mExternalService); 421 } 422 423 private class MockBinder extends Binder { 424 @Override 425 protected boolean onTransact(int code, Parcel data, Parcel reply, 426 int flags) throws RemoteException { 427 if (code == LocalService.STARTED_CODE) { 428 data.enforceInterface(LocalService.SERVICE_LOCAL); 429 int count = data.readInt(); 430 if (mExpectedServiceState == STATE_START_1) { 431 if (count == 1) { 432 finishGood(); 433 } else { 434 finishBad("onStart() again on an object when it " 435 + "should have been the first time"); 436 } 437 } else if (mExpectedServiceState == STATE_START_2) { 438 if (count == 2) { 439 finishGood(); 440 } else { 441 finishBad("onStart() the first time on an object when it " 442 + "should have been the second time"); 443 } 444 } else if (mExpectedServiceState == STATE_START_3) { 445 if (count == 3) { 446 finishGood(); 447 } else { 448 finishBad("onStart() the first time on an object when it " 449 + "should have been the third time"); 450 } 451 } else { 452 finishBad("onStart() was called when not expected (state=" 453 + mExpectedServiceState + ")"); 454 } 455 return true; 456 } else if (code == LocalService.DESTROYED_CODE) { 457 data.enforceInterface(LocalService.SERVICE_LOCAL); 458 if (mExpectedServiceState == STATE_DESTROY) { 459 finishGood(); 460 } else { 461 finishBad("onDestroy() was called when not expected (state=" 462 + mExpectedServiceState + ")"); 463 } 464 return true; 465 } else if (code == LocalService.UNBIND_CODE) { 466 data.enforceInterface(LocalService.SERVICE_LOCAL); 467 if (mExpectedServiceState == STATE_UNBIND) { 468 mExpectedServiceState = STATE_DESTROY; 469 } else if (mExpectedServiceState == STATE_UNBIND_ONLY) { 470 finishGood(); 471 } else { 472 finishBad("onUnbind() was called when not expected (state=" 473 + mExpectedServiceState + ")"); 474 } 475 return true; 476 } else if (code == LocalService.REBIND_CODE) { 477 data.enforceInterface(LocalService.SERVICE_LOCAL); 478 if (mExpectedServiceState == STATE_REBIND) { 479 finishGood(); 480 } else { 481 finishBad("onRebind() was called when not expected (state=" 482 + mExpectedServiceState + ")"); 483 } 484 return true; 485 } else { 486 return super.onTransact(code, data, reply, flags); 487 } 488 } 489 } 490 491 492 public void testLocalStartClass() throws Exception { 493 startExpectResult(mLocalService); 494 } 495 496 public void testLocalStartAction() throws Exception { 497 startExpectResult(new Intent( 498 LocalService.SERVICE_LOCAL, null /*uri*/, mContext, LocalService.class)); 499 } 500 501 public void testLocalBindClass() throws Exception { 502 bindExpectResult(mLocalService); 503 } 504 505 /* Just the Intent for a foreground service */ 506 private Intent foregroundServiceIntent(int command) { 507 return new Intent(mLocalForegroundService) 508 .putExtras(LocalForegroundService.newCommand(mStateReceiver, command)); 509 } 510 511 private void startForegroundService(int command) { 512 mContext.startService(foregroundServiceIntent(command)); 513 } 514 515 /* Start the service in a way that promises to go into the foreground */ 516 private void startRequiredForegroundService(int command) { 517 mContext.startForegroundService(foregroundServiceIntent(command)); 518 } 519 520 @MediumTest 521 public void testForegroundService_dontRemoveNotificationOnStop() throws Exception { 522 boolean success = false; 523 try { 524 // Start service as foreground - it should show notification #1 525 mExpectedServiceState = STATE_START_1; 526 startForegroundService(LocalForegroundService.COMMAND_START_FOREGROUND); 527 waitForResultOrThrow(DELAY, "service to start first time"); 528 assertNotification(1, LocalForegroundService.getNotificationTitle(1)); 529 530 // Stop foreground without removing notification - it should still show notification #1 531 mExpectedServiceState = STATE_START_2; 532 startForegroundService( 533 LocalForegroundService.COMMAND_STOP_FOREGROUND_DONT_REMOVE_NOTIFICATION); 534 waitForResultOrThrow(DELAY, "service to stop foreground"); 535 assertNotification(1, LocalForegroundService.getNotificationTitle(1)); 536 537 // Sends another notification reusing the same notification id. 538 String newTitle = "YODA I AM"; 539 sendNotification(1, newTitle); 540 assertNotification(1, newTitle); 541 542 // Start service as foreground again - it should kill notification #1 and show #2 543 mExpectedServiceState = STATE_START_3; 544 startForegroundService(LocalForegroundService.COMMAND_START_FOREGROUND); 545 waitForResultOrThrow(DELAY, "service to start foreground 2nd time"); 546 assertNoNotification(1); 547 assertNotification(2, LocalForegroundService.getNotificationTitle(2)); 548 549 success = true; 550 } finally { 551 if (!success) { 552 mContext.stopService(mLocalForegroundService); 553 } 554 } 555 mExpectedServiceState = STATE_DESTROY; 556 mContext.stopService(mLocalForegroundService); 557 waitForResultOrThrow(DELAY, "service to be destroyed"); 558 assertNoNotification(1); 559 assertNoNotification(2); 560 } 561 562 @MediumTest 563 public void testForegroundService_removeNotificationOnStop() throws Exception { 564 testForegroundServiceRemoveNotificationOnStop(false); 565 } 566 567 @MediumTest 568 public void testForegroundService_removeNotificationOnStopUsingFlags() throws Exception { 569 testForegroundServiceRemoveNotificationOnStop(true); 570 } 571 572 private void testForegroundServiceRemoveNotificationOnStop(boolean usingFlags) 573 throws Exception { 574 boolean success = false; 575 try { 576 // Start service as foreground - it should show notification #1 577 Log.d(TAG, "Expecting first start state..."); 578 mExpectedServiceState = STATE_START_1; 579 startForegroundService(LocalForegroundService.COMMAND_START_FOREGROUND); 580 waitForResultOrThrow(DELAY, "service to start first time"); 581 assertNotification(1, LocalForegroundService.getNotificationTitle(1)); 582 583 // Stop foreground removing notification 584 Log.d(TAG, "Expecting second start state..."); 585 mExpectedServiceState = STATE_START_2; 586 if (usingFlags) { 587 startForegroundService(LocalForegroundService 588 .COMMAND_STOP_FOREGROUND_REMOVE_NOTIFICATION_USING_FLAGS); 589 } else { 590 startForegroundService(LocalForegroundService 591 .COMMAND_STOP_FOREGROUND_REMOVE_NOTIFICATION); 592 } 593 waitForResultOrThrow(DELAY, "service to stop foreground"); 594 assertNoNotification(1); 595 596 // Start service as foreground again - it should show notification #2 597 mExpectedServiceState = STATE_START_3; 598 startForegroundService(LocalForegroundService.COMMAND_START_FOREGROUND); 599 waitForResultOrThrow(DELAY, "service to start as foreground 2nd time"); 600 assertNotification(2, LocalForegroundService.getNotificationTitle(2)); 601 602 success = true; 603 } finally { 604 if (!success) { 605 mContext.stopService(mLocalForegroundService); 606 } 607 } 608 mExpectedServiceState = STATE_DESTROY; 609 mContext.stopService(mLocalForegroundService); 610 waitForResultOrThrow(DELAY, "service to be destroyed"); 611 assertNoNotification(1); 612 assertNoNotification(2); 613 } 614 615 public void testRunningServices() throws Exception { 616 final int maxReturnedServices = 10; 617 final Bundle bundle = new Bundle(); 618 bundle.putParcelable(LocalService.REPORT_OBJ_NAME, new IBinderParcelable(mStateReceiver)); 619 620 boolean success = false; 621 622 ActivityManager am = mContext.getSystemService(ActivityManager.class); 623 624 // Put target app on whitelist so we can start its services. 625 SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(), 626 "cmd deviceidle whitelist +" + EXTERNAL_SERVICE_PACKAGE); 627 628 // No services should be reported back at the beginning 629 assertEquals(0, am.getRunningServices(maxReturnedServices).size()); 630 try { 631 mExpectedServiceState = STATE_START_1; 632 // Start external service. 633 mContext.startService(new Intent(mExternalService).putExtras(bundle)); 634 waitForResultOrThrow(DELAY, "external service to start first time"); 635 636 // Ensure we can't see service. 637 assertEquals(0, am.getRunningServices(maxReturnedServices).size()); 638 639 // Start local service. 640 mContext.startService(new Intent(mLocalService).putExtras(bundle)); 641 waitForResultOrThrow(DELAY, "local service to start first time"); 642 success = true; 643 644 // Ensure we can see service and it is ours. 645 List<ActivityManager.RunningServiceInfo> services = am.getRunningServices(maxReturnedServices); 646 assertEquals(1, services.size()); 647 assertEquals(android.os.Process.myUid(), services.get(0).uid); 648 } finally { 649 SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(), 650 "cmd deviceidle whitelist -" + EXTERNAL_SERVICE_PACKAGE); 651 if (!success) { 652 mContext.stopService(mLocalService); 653 mContext.stopService(mExternalService); 654 } 655 } 656 mExpectedServiceState = STATE_DESTROY; 657 658 mContext.stopService(mExternalService); 659 waitForResultOrThrow(DELAY, "external service to be destroyed"); 660 661 mContext.stopService(mLocalService); 662 waitForResultOrThrow(DELAY, "local service to be destroyed"); 663 664 // Once our service has stopped, make sure we can't see any services. 665 assertEquals(0, am.getRunningServices(maxReturnedServices).size()); 666 } 667 668 @MediumTest 669 public void testForegroundService_detachNotificationOnStop() throws Exception { 670 String newTitle = null; 671 boolean success = false; 672 try { 673 674 // Start service as foreground - it should show notification #1 675 mExpectedServiceState = STATE_START_1; 676 startForegroundService(LocalForegroundService.COMMAND_START_FOREGROUND); 677 waitForResultOrThrow(DELAY, "service to start first time"); 678 assertNotification(1, LocalForegroundService.getNotificationTitle(1)); 679 680 // Detaching notification 681 mExpectedServiceState = STATE_START_2; 682 startForegroundService( 683 LocalForegroundService.COMMAND_STOP_FOREGROUND_DETACH_NOTIFICATION); 684 waitForResultOrThrow(DELAY, "service to stop foreground"); 685 assertNotification(1, LocalForegroundService.getNotificationTitle(1)); 686 687 // Sends another notification reusing the same notification id. 688 newTitle = "YODA I AM"; 689 sendNotification(1, newTitle); 690 assertNotification(1, newTitle); 691 692 // Start service as foreground again - it should show notification #2.. 693 mExpectedServiceState = STATE_START_3; 694 startForegroundService(LocalForegroundService.COMMAND_START_FOREGROUND); 695 waitForResultOrThrow(DELAY, "service to start as foreground 2nd time"); 696 assertNotification(2, LocalForegroundService.getNotificationTitle(2)); 697 //...but keeping notification #1 698 assertNotification(1, newTitle); 699 700 success = true; 701 } finally { 702 if (!success) { 703 mContext.stopService(mLocalForegroundService); 704 } 705 } 706 mExpectedServiceState = STATE_DESTROY; 707 mContext.stopService(mLocalForegroundService); 708 waitForResultOrThrow(DELAY, "service to be destroyed"); 709 if (newTitle == null) { 710 assertNoNotification(1); 711 } else { 712 assertNotification(1, newTitle); 713 cancelNotification(1); 714 assertNoNotification(1); 715 } 716 assertNoNotification(2); 717 } 718 719 class TestSendCallback implements PendingIntent.OnFinished { 720 public volatile int result = -1; 721 722 @Override 723 public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode, 724 String resultData, Bundle resultExtras) { 725 Log.i(TAG, "foreground service PendingIntent callback got " + resultCode); 726 this.result = resultCode; 727 } 728 } 729 730 @MediumTest 731 public void testForegroundService_pendingIntentForeground() throws Exception { 732 boolean success = false; 733 734 PendingIntent pi = PendingIntent.getForegroundService(mContext, 1, 735 foregroundServiceIntent(LocalForegroundService.COMMAND_START_FOREGROUND), 736 PendingIntent.FLAG_CANCEL_CURRENT); 737 TestSendCallback callback = new TestSendCallback(); 738 739 try { 740 mExpectedServiceState = STATE_START_1; 741 pi.send(5038, callback, null); 742 waitForResultOrThrow(DELAY, "service to start first time"); 743 assertTrue(callback.result > -1); 744 745 success = true; 746 } finally { 747 if (!success) { 748 mContext.stopService(mLocalForegroundService); 749 } 750 } 751 752 mExpectedServiceState = STATE_DESTROY; 753 mContext.stopService(mLocalForegroundService); 754 waitForResultOrThrow(DELAY, "pendingintent service to be destroyed"); 755 } 756 757 @MediumTest 758 public void testLocalBindAction() throws Exception { 759 bindExpectResult(new Intent( 760 LocalService.SERVICE_LOCAL, null /*uri*/, mContext, LocalService.class)); 761 } 762 763 @MediumTest 764 public void testLocalBindAutoClass() throws Exception { 765 bindAutoExpectResult(mLocalService); 766 } 767 768 @MediumTest 769 public void testLocalBindAutoAction() throws Exception { 770 bindAutoExpectResult(new Intent( 771 LocalService.SERVICE_LOCAL, null /*uri*/, mContext, LocalService.class)); 772 } 773 774 @MediumTest 775 public void testLocalStartClassPermissions() throws Exception { 776 startExpectResult(mLocalGrantedService); 777 startExpectResult(mLocalDeniedService); 778 } 779 780 @MediumTest 781 public void testLocalStartActionPermissions() throws Exception { 782 startExpectResult(mLocalService_ApplicationHasPermission); 783 startExpectResult(mLocalService_ApplicationDoesNotHavePermission); 784 } 785 786 @MediumTest 787 public void testLocalBindClassPermissions() throws Exception { 788 bindExpectResult(mLocalGrantedService); 789 bindExpectResult(mLocalDeniedService); 790 } 791 792 @MediumTest 793 public void testLocalBindActionPermissions() throws Exception { 794 bindExpectResult(mLocalService_ApplicationHasPermission); 795 bindExpectResult(mLocalService_ApplicationDoesNotHavePermission); 796 } 797 798 @MediumTest 799 public void testLocalBindAutoClassPermissionGranted() throws Exception { 800 bindAutoExpectResult(mLocalGrantedService); 801 } 802 803 @MediumTest 804 public void testLocalBindAutoActionPermissionGranted() throws Exception { 805 bindAutoExpectResult(mLocalService_ApplicationHasPermission); 806 } 807 808 @MediumTest 809 public void testLocalUnbindTwice() throws Exception { 810 EmptyConnection conn = new EmptyConnection(); 811 mContext.bindService( 812 mLocalService_ApplicationHasPermission, conn, 0); 813 mContext.unbindService(conn); 814 try { 815 mContext.unbindService(conn); 816 fail("No exception thrown on the second unbind"); 817 } catch (IllegalArgumentException e) { 818 // expected 819 } 820 } 821 822 @MediumTest 823 public void testImplicitIntentFailsOnApiLevel21() throws Exception { 824 Intent intent = new Intent(LocalService.SERVICE_LOCAL); 825 EmptyConnection conn = new EmptyConnection(); 826 try { 827 mContext.bindService(intent, conn, 0); 828 mContext.unbindService(conn); 829 fail("Implicit intents should be disallowed for apps targeting API 21+"); 830 } catch (IllegalArgumentException e) { 831 // expected 832 } 833 } 834 } 835