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