Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2019 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.notification.legacy29.cts;
     18 
     19 import static junit.framework.Assert.assertEquals;
     20 import static junit.framework.Assert.assertTrue;
     21 import static junit.framework.TestCase.assertFalse;
     22 import static junit.framework.TestCase.assertNotNull;
     23 
     24 import static org.junit.Assert.assertNotEquals;
     25 import static org.junit.Assert.fail;
     26 
     27 import android.app.ActivityManager;
     28 import android.app.Instrumentation;
     29 import android.app.Notification;
     30 import android.app.NotificationChannel;
     31 import android.app.NotificationManager;
     32 import android.app.PendingIntent;
     33 import android.app.UiAutomation;
     34 import android.content.ComponentName;
     35 import android.content.Context;
     36 import android.content.Intent;
     37 import android.os.Bundle;
     38 import android.os.ParcelFileDescriptor;
     39 import android.provider.Telephony;
     40 import android.service.notification.Adjustment;
     41 import android.service.notification.NotificationAssistantService;
     42 import android.service.notification.NotificationListenerService;
     43 import android.service.notification.StatusBarNotification;
     44 
     45 import junit.framework.Assert;
     46 
     47 import org.junit.After;
     48 import org.junit.Before;
     49 import org.junit.Test;
     50 import org.junit.runner.RunWith;
     51 
     52 import java.io.FileInputStream;
     53 import java.io.IOException;
     54 import java.io.InputStream;
     55 import java.util.ArrayList;
     56 import java.util.List;
     57 import java.util.concurrent.TimeUnit;
     58 
     59 import androidx.test.InstrumentationRegistry;
     60 import androidx.test.runner.AndroidJUnit4;
     61 
     62 @RunWith(AndroidJUnit4.class)
     63 public class NotificationAssistantServiceTest {
     64 
     65     final String TAG = "NotAsstServiceTest";
     66     final String NOTIFICATION_CHANNEL_ID = "NotificationAssistantServiceTest";
     67     final int ICON_ID = android.R.drawable.sym_def_app_icon;
     68     final long SLEEP_TIME = 500; // milliseconds
     69 
     70     private TestNotificationAssistant mNotificationAssistantService;
     71     private TestNotificationListener mNotificationListenerService;
     72     private NotificationManager mNotificationManager;
     73     private ActivityManager mActivityManager;
     74     private Context mContext;
     75     private UiAutomation mUi;
     76 
     77     @Before
     78     public void setUp() throws IOException {
     79         mUi = InstrumentationRegistry.getInstrumentation().getUiAutomation();
     80         mContext = InstrumentationRegistry.getContext();
     81         mNotificationManager = (NotificationManager) mContext.getSystemService(
     82                 Context.NOTIFICATION_SERVICE);
     83         mNotificationManager.createNotificationChannel(new NotificationChannel(
     84                 NOTIFICATION_CHANNEL_ID, "name", NotificationManager.IMPORTANCE_DEFAULT));
     85         mActivityManager = mContext.getSystemService(ActivityManager.class);
     86     }
     87 
     88     @After
     89     public void tearDown() throws IOException {
     90         if (mNotificationListenerService != null) mNotificationListenerService.resetData();
     91         if (!mActivityManager.isLowRamDevice()) {
     92             toggleListenerAccess(false);
     93             toggleAssistantAccess(false);
     94         }
     95         mUi.dropShellPermissionIdentity();
     96     }
     97 
     98     @Test
     99     public void testOnNotificationEnqueued() throws Exception {
    100         if (mActivityManager.isLowRamDevice()) {
    101             return;
    102         }
    103         toggleListenerAccess(true);
    104         Thread.sleep(SLEEP_TIME);
    105 
    106         mUi.adoptShellPermissionIdentity("android.permission.STATUS_BAR_SERVICE");
    107         mNotificationManager.allowAssistantAdjustment(Adjustment.KEY_USER_SENTIMENT);
    108         mUi.dropShellPermissionIdentity();
    109 
    110         mNotificationListenerService = TestNotificationListener.getInstance();
    111 
    112         sendNotification(1, ICON_ID);
    113         StatusBarNotification sbn = getFirstNotificationFromPackage(TestNotificationListener.PKG);
    114         NotificationListenerService.Ranking out = new NotificationListenerService.Ranking();
    115         mNotificationListenerService.mRankingMap.getRanking(sbn.getKey(), out);
    116 
    117         // No modification because the Notification Assistant is not enabled
    118         assertEquals(NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL,
    119                 out.getUserSentiment());
    120         mNotificationListenerService.resetData();
    121 
    122         toggleAssistantAccess(true);
    123         Thread.sleep(SLEEP_TIME); // wait for listener and assistant to be allowed
    124         mNotificationAssistantService = TestNotificationAssistant.getInstance();
    125 
    126         sendNotification(1, ICON_ID);
    127         sbn = getFirstNotificationFromPackage(TestNotificationListener.PKG);
    128         mNotificationListenerService.mRankingMap.getRanking(sbn.getKey(), out);
    129 
    130         // Assistant modifies notification
    131         assertEquals(NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE,
    132                 out.getUserSentiment());
    133     }
    134 
    135     @Test
    136     public void testAdjustNotification_userSentimentKey() throws Exception {
    137         if (mActivityManager.isLowRamDevice()) {
    138             return;
    139         }
    140         setUpListeners();
    141 
    142         mUi.adoptShellPermissionIdentity("android.permission.STATUS_BAR_SERVICE");
    143         mNotificationManager.allowAssistantAdjustment(Adjustment.KEY_USER_SENTIMENT);
    144         mUi.dropShellPermissionIdentity();
    145 
    146         sendNotification(1, ICON_ID);
    147         StatusBarNotification sbn = getFirstNotificationFromPackage(TestNotificationListener.PKG);
    148         NotificationListenerService.Ranking out = new NotificationListenerService.Ranking();
    149         mNotificationListenerService.mRankingMap.getRanking(sbn.getKey(), out);
    150 
    151         assertEquals(NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE,
    152                 out.getUserSentiment());
    153 
    154         Bundle signals = new Bundle();
    155         signals.putInt(Adjustment.KEY_USER_SENTIMENT,
    156                 NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
    157         Adjustment adjustment = new Adjustment(sbn.getPackageName(), sbn.getKey(), signals, "",
    158                 sbn.getUser());
    159 
    160         mNotificationAssistantService.adjustNotification(adjustment);
    161         Thread.sleep(SLEEP_TIME); // wait for adjustment to be processed
    162 
    163         mNotificationListenerService.mRankingMap.getRanking(sbn.getKey(), out);
    164 
    165         assertEquals(NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE,
    166                 out.getUserSentiment());
    167     }
    168 
    169     @Test
    170     public void testAdjustNotification_importanceKey() throws Exception {
    171         if (mActivityManager.isLowRamDevice()) {
    172             return;
    173         }
    174         setUpListeners();
    175 
    176         mUi.adoptShellPermissionIdentity("android.permission.STATUS_BAR_SERVICE");
    177         mNotificationManager.allowAssistantAdjustment(Adjustment.KEY_IMPORTANCE);
    178         mUi.dropShellPermissionIdentity();
    179 
    180         sendNotification(1, ICON_ID);
    181         StatusBarNotification sbn = getFirstNotificationFromPackage(TestNotificationListener.PKG);
    182         NotificationListenerService.Ranking out = new NotificationListenerService.Ranking();
    183         mNotificationListenerService.mRankingMap.getRanking(sbn.getKey(), out);
    184 
    185         int currentImportance = out.getImportance();
    186         int newImportance = currentImportance == NotificationManager.IMPORTANCE_DEFAULT
    187                 ? NotificationManager.IMPORTANCE_HIGH : NotificationManager.IMPORTANCE_DEFAULT;
    188 
    189         Bundle signals = new Bundle();
    190         signals.putInt(Adjustment.KEY_IMPORTANCE, newImportance);
    191         Adjustment adjustment = new Adjustment(sbn.getPackageName(), sbn.getKey(), signals, "",
    192                 sbn.getUser());
    193 
    194         mNotificationAssistantService.adjustNotification(adjustment);
    195         Thread.sleep(SLEEP_TIME); // wait for adjustment to be processed
    196 
    197         mNotificationListenerService.mRankingMap.getRanking(sbn.getKey(), out);
    198 
    199         assertEquals(newImportance, out.getImportance());
    200     }
    201 
    202     @Test
    203     public void testAdjustNotification_smartActionKey() throws Exception {
    204         if (mActivityManager.isLowRamDevice()) {
    205             return;
    206         }
    207         setUpListeners();
    208 
    209         mUi.adoptShellPermissionIdentity("android.permission.STATUS_BAR_SERVICE");
    210         mNotificationManager.allowAssistantAdjustment(Adjustment.KEY_CONTEXTUAL_ACTIONS);
    211         mUi.dropShellPermissionIdentity();
    212 
    213         PendingIntent sendIntent = PendingIntent.getActivity(mContext, 0,
    214                 new Intent(Intent.ACTION_SEND), 0);
    215         Notification.Action sendAction = new Notification.Action.Builder(ICON_ID, "SEND",
    216                 sendIntent).build();
    217 
    218         sendNotification(1, ICON_ID);
    219         StatusBarNotification sbn = getFirstNotificationFromPackage(TestNotificationListener.PKG);
    220         NotificationListenerService.Ranking out = new NotificationListenerService.Ranking();
    221         mNotificationListenerService.mRankingMap.getRanking(sbn.getKey(), out);
    222 
    223         List<Notification.Action> smartActions = out.getSmartActions();
    224         if (smartActions != null) {
    225             for (int i = 0; i < smartActions.size(); i++) {
    226                 Notification.Action action = smartActions.get(i);
    227                 assertNotEquals(sendIntent, action.actionIntent);
    228             }
    229         }
    230 
    231         ArrayList<Notification.Action> extraAction = new ArrayList<>();
    232         extraAction.add(sendAction);
    233         Bundle signals = new Bundle();
    234         signals.putParcelableArrayList(Adjustment.KEY_CONTEXTUAL_ACTIONS, extraAction);
    235         Adjustment adjustment = new Adjustment(sbn.getPackageName(), sbn.getKey(), signals, "",
    236                 sbn.getUser());
    237 
    238         mNotificationAssistantService.adjustNotification(adjustment);
    239         Thread.sleep(SLEEP_TIME); //wait for adjustment to be processed
    240 
    241         mNotificationListenerService.mRankingMap.getRanking(sbn.getKey(), out);
    242 
    243         boolean actionFound = false;
    244         smartActions = out.getSmartActions();
    245         for (int i = 0; i < smartActions.size(); i++) {
    246             Notification.Action action = smartActions.get(i);
    247             actionFound = actionFound || action.actionIntent.equals(sendIntent);
    248         }
    249         assertTrue(actionFound);
    250     }
    251 
    252     @Test
    253     public void testAdjustNotification_smartReplyKey() throws Exception {
    254         if (mActivityManager.isLowRamDevice()) {
    255             return;
    256         }
    257         setUpListeners();
    258         CharSequence smartReply = "Smart Reply!";
    259 
    260         mUi.adoptShellPermissionIdentity("android.permission.STATUS_BAR_SERVICE");
    261         mNotificationManager.allowAssistantAdjustment(Adjustment.KEY_TEXT_REPLIES);
    262         mUi.dropShellPermissionIdentity();
    263 
    264         sendNotification(1, ICON_ID);
    265         StatusBarNotification sbn = getFirstNotificationFromPackage(TestNotificationListener.PKG);
    266         NotificationListenerService.Ranking out = new NotificationListenerService.Ranking();
    267         mNotificationListenerService.mRankingMap.getRanking(sbn.getKey(), out);
    268 
    269         List<CharSequence> smartReplies = out.getSmartReplies();
    270         if (smartReplies != null) {
    271             for (int i = 0; i < smartReplies.size(); i++) {
    272                 CharSequence reply = smartReplies.get(i);
    273                 assertNotEquals(smartReply, reply);
    274             }
    275         }
    276 
    277         ArrayList<CharSequence> extraReply = new ArrayList<>();
    278         extraReply.add(smartReply);
    279         Bundle signals = new Bundle();
    280         signals.putCharSequenceArrayList(Adjustment.KEY_TEXT_REPLIES, extraReply);
    281         Adjustment adjustment = new Adjustment(sbn.getPackageName(), sbn.getKey(), signals, "",
    282                 sbn.getUser());
    283 
    284         mNotificationAssistantService.adjustNotification(adjustment);
    285         Thread.sleep(SLEEP_TIME); //wait for adjustment to be processed
    286 
    287         mNotificationListenerService.mRankingMap.getRanking(sbn.getKey(), out);
    288 
    289         boolean replyFound = false;
    290         smartReplies = out.getSmartReplies();
    291         for (int i = 0; i < smartReplies.size(); i++) {
    292             CharSequence reply = smartReplies.get(i);
    293             replyFound = replyFound || reply.equals(smartReply);
    294         }
    295         assertTrue(replyFound);
    296     }
    297 
    298     @Test
    299     public void testAdjustNotification_importanceKey_notAllowed() throws Exception {
    300         if (mActivityManager.isLowRamDevice()) {
    301             return;
    302         }
    303         setUpListeners();
    304 
    305         mUi.adoptShellPermissionIdentity("android.permission.STATUS_BAR_SERVICE");
    306         mNotificationManager.disallowAssistantAdjustment(Adjustment.KEY_IMPORTANCE);
    307         mUi.dropShellPermissionIdentity();
    308 
    309         sendNotification(1, ICON_ID);
    310         StatusBarNotification sbn = getFirstNotificationFromPackage(
    311                 TestNotificationListener.PKG);
    312         NotificationListenerService.Ranking out = new NotificationListenerService.Ranking();
    313         mNotificationListenerService.mRankingMap.getRanking(sbn.getKey(), out);
    314 
    315         int currentImportance = out.getImportance();
    316         int newImportance = currentImportance == NotificationManager.IMPORTANCE_DEFAULT
    317                 ? NotificationManager.IMPORTANCE_HIGH : NotificationManager.IMPORTANCE_DEFAULT;
    318 
    319         Bundle signals = new Bundle();
    320         signals.putInt(Adjustment.KEY_IMPORTANCE, newImportance);
    321         Adjustment adjustment = new Adjustment(sbn.getPackageName(), sbn.getKey(), signals, "",
    322                 sbn.getUser());
    323 
    324         mNotificationAssistantService.adjustNotification(adjustment);
    325         Thread.sleep(SLEEP_TIME); // wait for adjustment to be processed
    326 
    327         mNotificationListenerService.mRankingMap.getRanking(sbn.getKey(), out);
    328 
    329         assertEquals(currentImportance, out.getImportance());
    330 
    331     }
    332 
    333     @Test
    334     public void testGetAllowedAssistantCapabilities_permission() throws Exception {
    335         if (mActivityManager.isLowRamDevice()) {
    336             return;
    337         }
    338         toggleAssistantAccess(false);
    339 
    340         try {
    341             mNotificationManager.getAllowedAssistantAdjustments();
    342             fail(" Non assistants cannot call this method");
    343         } catch (SecurityException e) {
    344             //pass
    345         }
    346     }
    347 
    348     @Test
    349     public void testGetAllowedAssistantCapabilities() throws Exception {
    350         if (mActivityManager.isLowRamDevice()) {
    351             return;
    352         }
    353         toggleAssistantAccess(true);
    354         Thread.sleep(SLEEP_TIME); // wait for assistant to be allowed
    355         mNotificationAssistantService = TestNotificationAssistant.getInstance();
    356         mNotificationAssistantService.onAllowedAdjustmentsChanged();
    357         assertNotNull(mNotificationAssistantService.currentCapabilities);
    358 
    359         mUi.adoptShellPermissionIdentity("android.permission.STATUS_BAR_SERVICE");
    360         mNotificationManager.allowAssistantAdjustment(Adjustment.KEY_SNOOZE_CRITERIA);
    361 
    362         Thread.sleep(SLEEP_TIME);
    363         assertTrue(mNotificationAssistantService.currentCapabilities.contains(
    364                 Adjustment.KEY_SNOOZE_CRITERIA));
    365 
    366         mNotificationManager.disallowAssistantAdjustment(Adjustment.KEY_SNOOZE_CRITERIA);
    367         Thread.sleep(SLEEP_TIME);
    368         assertFalse(mNotificationAssistantService.currentCapabilities.contains(
    369                 Adjustment.KEY_SNOOZE_CRITERIA));
    370 
    371         // just in case KEY_SNOOZE_CRITERIA was included in the original set, test adding again
    372         mNotificationManager.allowAssistantAdjustment(Adjustment.KEY_SNOOZE_CRITERIA);
    373         Thread.sleep(SLEEP_TIME);
    374         assertTrue(mNotificationAssistantService.currentCapabilities.contains(
    375                 Adjustment.KEY_SNOOZE_CRITERIA));
    376 
    377         mUi.dropShellPermissionIdentity();
    378     }
    379 
    380     @Test
    381     public void testOnActionInvoked_methodExists() throws Exception {
    382         if (mActivityManager.isLowRamDevice()) {
    383             return;
    384         }
    385         setUpListeners();
    386         final Intent intent = new Intent(Intent.ACTION_MAIN, Telephony.Threads.CONTENT_URI);
    387 
    388         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP
    389                 | Intent.FLAG_ACTIVITY_CLEAR_TOP);
    390         intent.setAction(Intent.ACTION_MAIN);
    391 
    392         final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
    393         Notification.Action action = new Notification.Action.Builder(null, "",
    394                 pendingIntent).build();
    395         // This method has to exist and the call cannot fail
    396         mNotificationAssistantService.onActionInvoked("", action,
    397                 NotificationAssistantService.SOURCE_FROM_APP);
    398     }
    399 
    400     @Test
    401     public void testOnNotificationDirectReplied_methodExists() throws Exception {
    402         if (mActivityManager.isLowRamDevice()) {
    403             return;
    404         }
    405         setUpListeners();
    406         // This method has to exist and the call cannot fail
    407         mNotificationAssistantService.onNotificationDirectReplied("");
    408     }
    409 
    410     @Test
    411     public void testOnNotificationExpansionChanged_methodExists() throws Exception {
    412         if (mActivityManager.isLowRamDevice()) {
    413             return;
    414         }
    415         setUpListeners();
    416         // This method has to exist and the call cannot fail
    417         mNotificationAssistantService.onNotificationExpansionChanged("", true, true);
    418     }
    419 
    420     @Test
    421     public void testOnNotificationsSeen_methodExists() throws Exception {
    422         if (mActivityManager.isLowRamDevice()) {
    423             return;
    424         }
    425         setUpListeners();
    426         // This method has to exist and the call cannot fail
    427         mNotificationAssistantService.onNotificationsSeen(new ArrayList<>());
    428     }
    429 
    430     @Test
    431     public void testOnSuggestedReplySent_methodExists() throws Exception {
    432         if (mActivityManager.isLowRamDevice()) {
    433             return;
    434         }
    435         setUpListeners();
    436         // This method has to exist and the call cannot fail
    437         mNotificationAssistantService.onSuggestedReplySent("", "",
    438                 NotificationAssistantService.SOURCE_FROM_APP);
    439     }
    440 
    441     private StatusBarNotification getFirstNotificationFromPackage(String PKG)
    442             throws InterruptedException {
    443         StatusBarNotification sbn = mNotificationListenerService.mPosted.poll(SLEEP_TIME,
    444                 TimeUnit.MILLISECONDS);
    445         assertNotNull(sbn);
    446         while (!sbn.getPackageName().equals(PKG)) {
    447             sbn = mNotificationListenerService.mPosted.poll(SLEEP_TIME, TimeUnit.MILLISECONDS);
    448         }
    449         assertNotNull(sbn);
    450         return sbn;
    451     }
    452 
    453     private void setUpListeners() throws Exception {
    454         toggleListenerAccess(true);
    455         toggleAssistantAccess(true);
    456         Thread.sleep(2 * SLEEP_TIME); // wait for listener and assistant to be allowed
    457 
    458         mNotificationListenerService = TestNotificationListener.getInstance();
    459         mNotificationAssistantService = TestNotificationAssistant.getInstance();
    460 
    461         assertNotNull(mNotificationListenerService);
    462         assertNotNull(mNotificationAssistantService);
    463     }
    464 
    465     private void sendNotification(final int id, final int icon) throws Exception {
    466         sendNotification(id, null, icon);
    467     }
    468 
    469     private void sendNotification(final int id, String groupKey, final int icon) throws Exception {
    470         final Intent intent = new Intent(Intent.ACTION_MAIN, Telephony.Threads.CONTENT_URI);
    471 
    472         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP
    473                 | Intent.FLAG_ACTIVITY_CLEAR_TOP);
    474         intent.setAction(Intent.ACTION_MAIN);
    475 
    476         final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
    477         final Notification notification =
    478                 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
    479                         .setSmallIcon(icon)
    480                         .setWhen(System.currentTimeMillis())
    481                         .setContentTitle("notify#" + id)
    482                         .setContentText("This is #" + id + "notification  ")
    483                         .setContentIntent(pendingIntent)
    484                         .setGroup(groupKey)
    485                         .build();
    486         mNotificationManager.notify(id, notification);
    487     }
    488 
    489     private void toggleListenerAccess(boolean on) throws IOException {
    490 
    491         Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
    492         String componentName = TestNotificationListener.getId();
    493 
    494         String command = " cmd notification " + (on ? "allow_listener " : "disallow_listener ")
    495                 + componentName;
    496 
    497         runCommand(command, instrumentation);
    498 
    499         final ComponentName listenerComponent = TestNotificationListener.getComponentName();
    500         final NotificationManager nm = mContext.getSystemService(NotificationManager.class);
    501         Assert.assertTrue(listenerComponent + " has not been " + (on ? "allowed" : "disallowed"),
    502                 nm.isNotificationListenerAccessGranted(listenerComponent) == on);
    503     }
    504 
    505     private void toggleAssistantAccess(boolean on) {
    506         final ComponentName assistantComponent = TestNotificationAssistant.getComponentName();
    507 
    508         mUi.adoptShellPermissionIdentity("android.permission.STATUS_BAR_SERVICE",
    509                 "android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE");
    510         mNotificationManager.setNotificationAssistantAccessGranted(assistantComponent, on);
    511 
    512         assertTrue(assistantComponent + " has not been " + (on ? "allowed" : "disallowed"),
    513                 mNotificationManager.isNotificationAssistantAccessGranted(assistantComponent)
    514                         == on);
    515         if (on) {
    516             assertEquals(assistantComponent,
    517                     mNotificationManager.getAllowedNotificationAssistant());
    518         } else {
    519             assertNotEquals(assistantComponent,
    520                     mNotificationManager.getAllowedNotificationAssistant());
    521         }
    522 
    523         mUi.dropShellPermissionIdentity();
    524     }
    525 
    526     private void runCommand(String command, Instrumentation instrumentation) throws IOException {
    527         UiAutomation uiAutomation = instrumentation.getUiAutomation();
    528         // Execute command
    529         try (ParcelFileDescriptor fd = uiAutomation.executeShellCommand(command)) {
    530             assertNotNull("Failed to execute shell command: " + command, fd);
    531             // Wait for the command to finish by reading until EOF
    532             try (InputStream in = new FileInputStream(fd.getFileDescriptor())) {
    533                 byte[] buffer = new byte[4096];
    534                 while (in.read(buffer) > 0) {
    535                 }
    536             } catch (IOException e) {
    537                 throw new IOException("Could not read stdout of command:" + command, e);
    538             }
    539         }
    540     }
    541 }