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.car; 18 19 import android.annotation.FloatRange; 20 import android.annotation.IntDef; 21 import android.annotation.NonNull; 22 import android.annotation.RequiresPermission; 23 import android.annotation.SystemApi; 24 import android.os.Handler; 25 import android.os.IBinder; 26 import android.os.ParcelFileDescriptor; 27 import android.os.RemoteException; 28 29 import com.android.internal.annotations.VisibleForTesting; 30 31 import libcore.io.IoUtils; 32 33 import java.lang.annotation.Retention; 34 import java.lang.annotation.RetentionPolicy; 35 import java.lang.ref.WeakReference; 36 import java.util.Objects; 37 38 /** 39 * Car specific bugreport manager. Only available for userdebug and eng builds. 40 * @hide 41 */ 42 @SystemApi 43 public final class CarBugreportManager extends CarManagerBase { 44 45 private final ICarBugreportService mService; 46 47 /** 48 * Callback from carbugreport manager. Callback methods are always called on the main thread. 49 */ 50 public abstract static class CarBugreportManagerCallback { 51 /** 52 * @hide 53 */ 54 @Retention(RetentionPolicy.SOURCE) 55 @IntDef(prefix = {"CAR_BUGREPORT_ERROR_"}, value = { 56 CAR_BUGREPORT_DUMPSTATE_FAILED, 57 CAR_BUGREPORT_IN_PROGRESS, 58 CAR_BUGREPORT_DUMPSTATE_CONNECTION_FAILED, 59 CAR_BUGREPORT_SERVICE_NOT_AVAILABLE 60 }) 61 62 public @interface CarBugreportErrorCode { 63 } 64 65 /** Dumpstate failed to generate bugreport. 66 */ 67 public static final int CAR_BUGREPORT_DUMPSTATE_FAILED = 1; 68 69 /** 70 * Another bugreport is in progress. 71 */ 72 public static final int CAR_BUGREPORT_IN_PROGRESS = 2; 73 74 /** Cannot connect to dumpstate 75 */ 76 public static final int CAR_BUGREPORT_DUMPSTATE_CONNECTION_FAILED = 3; 77 78 /** Car bugreport service is not available (true for user builds) 79 */ 80 public static final int CAR_BUGREPORT_SERVICE_NOT_AVAILABLE = 4; 81 82 /** 83 * Called when bugreport progress changes. 84 * 85 * <p>It's never called after {@link #onError} or {@link #onFinished}. 86 * 87 * @param progress - a number in [0.0, 100.0]. 88 */ onProgress(@loatRangefrom = 0f, to = 100f) float progress)89 public void onProgress(@FloatRange(from = 0f, to = 100f) float progress) { 90 } 91 92 /** 93 * Called on an error condition with one of the error codes listed above. 94 * 95 * @param errorCode the error code that defines failure reason. 96 */ onError(@arBugreportErrorCode int errorCode)97 public void onError(@CarBugreportErrorCode int errorCode) { 98 } 99 100 /** 101 * Called when taking bugreport finishes successfully. 102 */ onFinished()103 public void onFinished() { 104 } 105 } 106 107 /** 108 * Internal wrapper class to service. 109 * @hide 110 */ 111 private static final class CarBugreportManagerCallbackWrapper extends 112 ICarBugreportCallback.Stub { 113 114 private final WeakReference<CarBugreportManagerCallback> mWeakCallback; 115 private final WeakReference<Handler> mWeakHandler; 116 117 /** 118 * Create a new callback wrapper. 119 * 120 * @param callback the callback passed from app 121 * @param handler the handler to execute callbacks on 122 */ CarBugreportManagerCallbackWrapper(CarBugreportManagerCallback callback, Handler handler)123 CarBugreportManagerCallbackWrapper(CarBugreportManagerCallback callback, 124 Handler handler) { 125 mWeakCallback = new WeakReference<>(callback); 126 mWeakHandler = new WeakReference<>(handler); 127 } 128 129 @Override onProgress(@loatRangefrom = 0f, to = 100f) float progress)130 public void onProgress(@FloatRange(from = 0f, to = 100f) float progress) { 131 CarBugreportManagerCallback callback = mWeakCallback.get(); 132 Handler handler = mWeakHandler.get(); 133 if (handler != null && callback != null) { 134 handler.post(() -> callback.onProgress(progress)); 135 } 136 } 137 138 @Override onError(@arBugreportManagerCallback.CarBugreportErrorCode int errorCode)139 public void onError(@CarBugreportManagerCallback.CarBugreportErrorCode int errorCode) { 140 CarBugreportManagerCallback callback = mWeakCallback.get(); 141 Handler handler = mWeakHandler.get(); 142 if (handler != null && callback != null) { 143 handler.post(() -> callback.onError(errorCode)); 144 } 145 } 146 147 @Override onFinished()148 public void onFinished() { 149 CarBugreportManagerCallback callback = mWeakCallback.get(); 150 Handler handler = mWeakHandler.get(); 151 if (handler != null && callback != null) { 152 handler.post(callback::onFinished); 153 } 154 } 155 } 156 157 /** 158 * Get an instance of the CarBugreportManager 159 * 160 * Should not be obtained directly by clients, use {@link Car#getCarManager(String)} instead. 161 * @hide 162 */ CarBugreportManager(Car car, IBinder service)163 public CarBugreportManager(Car car, IBinder service) { 164 super(car); 165 mService = ICarBugreportService.Stub.asInterface(service); 166 } 167 168 /** 169 * Request a bug report. A zipped (i.e. legacy) bugreport is generated in the background 170 * using dumpstate. This API also generates extra files that do not exist in the legacy 171 * bugreport and makes them available through an extra output file. Currently the extra 172 * output contains the screenshots for all the physical displays. 173 * 174 * <p>It closes provided file descriptors. The callback runs on a background thread. 175 * 176 * <p>This method is enabled only for allowed bug reporting apps. It can be configured 177 * using {@code config_car_bugreport_applications} array that is defined in 178 * {@code packages/services/Car/service/res/values/config.xml}. 179 * 180 * @param output the zipped bugreport file. 181 * @param extraOutput a zip file that contains extra files generated for automotive. 182 * @param callback the callback for reporting dump status. 183 */ 184 @RequiresPermission(android.Manifest.permission.DUMP) requestBugreport( @onNull ParcelFileDescriptor output, @NonNull ParcelFileDescriptor extraOutput, @NonNull CarBugreportManagerCallback callback)185 public void requestBugreport( 186 @NonNull ParcelFileDescriptor output, 187 @NonNull ParcelFileDescriptor extraOutput, 188 @NonNull CarBugreportManagerCallback callback) { 189 requestBugreport(output, extraOutput, callback, /* dumpstateDryRun= */ false); 190 } 191 192 /** 193 * Similar to {@link requestBugreport()} above, but runs with {@code dumpstateDryRun=true}. 194 * 195 * @hide 196 */ 197 @RequiresPermission(android.Manifest.permission.DUMP) 198 @VisibleForTesting requestBugreportForTesting( @onNull ParcelFileDescriptor output, @NonNull ParcelFileDescriptor extraOutput, @NonNull CarBugreportManagerCallback callback)199 public void requestBugreportForTesting( 200 @NonNull ParcelFileDescriptor output, 201 @NonNull ParcelFileDescriptor extraOutput, 202 @NonNull CarBugreportManagerCallback callback) { 203 requestBugreport(output, extraOutput, callback, /* dumpstateDryRun= */ true); 204 } 205 206 /** 207 * Requests a bug report. 208 * 209 * @param dumpstateDryRun if true, it runs dumpstate in dry_run mode, which is faster. 210 */ 211 @RequiresPermission(android.Manifest.permission.DUMP) requestBugreport( @onNull ParcelFileDescriptor output, @NonNull ParcelFileDescriptor extraOutput, @NonNull CarBugreportManagerCallback callback, boolean dumpstateDryRun)212 private void requestBugreport( 213 @NonNull ParcelFileDescriptor output, 214 @NonNull ParcelFileDescriptor extraOutput, 215 @NonNull CarBugreportManagerCallback callback, 216 boolean dumpstateDryRun) { 217 Objects.requireNonNull(output); 218 Objects.requireNonNull(extraOutput); 219 Objects.requireNonNull(callback); 220 try { 221 CarBugreportManagerCallbackWrapper wrapper = 222 new CarBugreportManagerCallbackWrapper(callback, getEventHandler()); 223 mService.requestBugreport(output, extraOutput, wrapper, dumpstateDryRun); 224 } catch (RemoteException e) { 225 handleRemoteExceptionFromCarService(e); 226 } finally { 227 // Safely close the FDs on this side, because binder dups them. 228 IoUtils.closeQuietly(output); 229 IoUtils.closeQuietly(extraOutput); 230 } 231 } 232 233 /** 234 * Cancels the running bugreport. It doesn't guarantee immediate cancellation and after 235 * calling this method, callbacks provided in {@link #requestBugreport} might still get fired. 236 * The next {@link startBugreport} should be called after a delay to allow the system to fully 237 * complete the cancellation. 238 */ 239 @RequiresPermission(android.Manifest.permission.DUMP) cancelBugreport()240 public void cancelBugreport() { 241 try { 242 mService.cancelBugreport(); 243 } catch (RemoteException e) { 244 handleRemoteExceptionFromCarService(e); 245 } 246 } 247 248 /** 249 * @hide 250 */ 251 @Override onCarDisconnected()252 public void onCarDisconnected() { 253 } 254 } 255