Home | History | Annotate | Download | only in cts
      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