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