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