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