1 /* 2 * Copyright (C) 2021 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.car.builtin.os; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.annotation.SystemApi; 22 import android.car.builtin.annotation.AddedIn; 23 import android.car.builtin.annotation.PlatformVersion; 24 import android.os.Binder; 25 import android.os.IBinder; 26 import android.os.Parcel; 27 import android.os.ParcelFileDescriptor; 28 import android.os.RemoteCallbackList; 29 import android.os.RemoteException; 30 import android.os.ResultReceiver; 31 import android.os.ShellCallback; 32 33 import com.android.internal.util.FastPrintWriter; 34 35 import libcore.io.IoUtils; 36 37 import java.io.FileDescriptor; 38 import java.io.FileInputStream; 39 import java.io.FileOutputStream; 40 import java.io.IOException; 41 import java.io.PrintWriter; 42 43 /** 44 * Helper for Binder related usage 45 * 46 * @hide 47 */ 48 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) 49 public final class BinderHelper { 50 51 /** Dumps given {@link RemoteCallbackList} for debugging. */ 52 @AddedIn(PlatformVersion.TIRAMISU_0) dumpRemoteCallbackList(@onNull RemoteCallbackList<?> list, @NonNull PrintWriter pw)53 public static void dumpRemoteCallbackList(@NonNull RemoteCallbackList<?> list, 54 @NonNull PrintWriter pw) { 55 list.dump(pw, /* prefix= */ ""); 56 } 57 BinderHelper()58 private BinderHelper() { 59 throw new UnsupportedOperationException("contains only static members"); 60 } 61 62 /** 63 * Listener for implementing shell command handling. Should be used with 64 * {@link #onTransactForCmd(int, Parcel, Parcel, int, ShellCommandListener)}. 65 */ 66 public interface ShellCommandListener { 67 /** 68 * Implements shell command 69 * @param in input file 70 * @param out output file 71 * @param err error output 72 * @param args args passed with the command 73 * 74 * @return linux error code for the binder call. {@code 0} means ok. 75 */ 76 @AddedIn(PlatformVersion.TIRAMISU_0) onShellCommand(@onNull FileDescriptor in, @NonNull FileDescriptor out, @NonNull FileDescriptor err, @NonNull String[] args)77 int onShellCommand(@NonNull FileDescriptor in, @NonNull FileDescriptor out, 78 @NonNull FileDescriptor err, @NonNull String[] args); 79 } 80 81 /** 82 * Handles {@link Binder#onTransact(int, Parcel, Parcel, int)} for shell command. 83 * 84 * <p>This is different from the default {@link Binder#onTransact(int, Parcel, Parcel, int)} 85 * in that this does not check shell UID so that test apps not having shell UID can use it. Note 86 * that underlying command still should do necessary permission checks so that only apps with 87 * right permission can run that command. 88 * 89 * @param code Binder call code 90 * @param data Input {@code Parcel} 91 * @param reply Reply {@code Parcel} 92 * @param flags Binder de-serialization flags 93 * @param cmdListener Listener to implement the command. 94 * 95 * @return {@code true} if the transaction was handled (=if it is dump or cmd call). 96 * 97 * @throws RemoteException for binder call failure 98 */ 99 @AddedIn(PlatformVersion.TIRAMISU_0) onTransactForCmd(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags, @NonNull ShellCommandListener cmdListener)100 public static boolean onTransactForCmd(int code, @NonNull Parcel data, 101 @Nullable Parcel reply, int flags, @NonNull ShellCommandListener cmdListener) 102 throws RemoteException { 103 if (code == IBinder.SHELL_COMMAND_TRANSACTION) { 104 ParcelFileDescriptor in = data.readFileDescriptor(); 105 ParcelFileDescriptor out = data.readFileDescriptor(); 106 ParcelFileDescriptor err = data.readFileDescriptor(); 107 String[] args = data.readStringArray(); 108 // not used but should read from Parcel to get the next one. 109 ShellCallback.CREATOR.createFromParcel(data); 110 ResultReceiver resultReceiver = ResultReceiver.CREATOR.createFromParcel(data); 111 if (args == null) { 112 args = new String[0]; 113 } 114 115 FileDescriptor errFd; 116 if (err == null) { 117 // if no err, use out for err 118 errFd = out.getFileDescriptor(); 119 } else { 120 errFd = err.getFileDescriptor(); 121 } 122 FileInputStream inputStream = null; 123 try { 124 FileDescriptor inFd; 125 if (in == null) { 126 inputStream = new FileInputStream("/dev/null"); 127 inFd = inputStream.getFD(); 128 } else { 129 inFd = in.getFileDescriptor(); 130 } 131 if (out != null) { 132 int r = cmdListener.onShellCommand(inFd, out.getFileDescriptor(), errFd, args); 133 if (resultReceiver != null) { 134 resultReceiver.send(r, null); 135 } 136 } 137 } catch (Exception e) { 138 sendFailureToCaller(errFd, resultReceiver, 139 "Cannot handle command with error:" + e.getMessage()); 140 } finally { 141 try { 142 if (inputStream != null) { 143 inputStream.close(); 144 } 145 } catch (IOException e) { 146 sendFailureToCaller(errFd, resultReceiver, 147 "I/O error:" + e.getMessage()); 148 // Still continue and complete the command (return true} 149 } 150 IoUtils.closeQuietly(in); 151 IoUtils.closeQuietly(out); 152 IoUtils.closeQuietly(err); 153 if (reply != null) { 154 reply.writeNoException(); 155 } 156 } 157 return true; 158 } 159 return false; 160 } 161 sendFailureToCaller(FileDescriptor errFd, ResultReceiver receiver, String msg)162 private static void sendFailureToCaller(FileDescriptor errFd, ResultReceiver receiver, 163 String msg) { 164 try (PrintWriter pw = new FastPrintWriter(new FileOutputStream(errFd))) { 165 pw.println(msg); 166 pw.flush(); 167 } 168 receiver.send(/* resultCode= */ -1, /* resultData= */ null); 169 } 170 } 171