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