blob: 672624cab88c1ed14a378ea3fcb8b559f066041f [file] [log] [blame]
Nandana Dutt3386fb72018-12-12 17:26:57 +00001/*
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
17package android.os;
18
Nandana Dutt28d8dd72019-01-25 14:31:05 +000019import android.annotation.CallbackExecutor;
Abhijeet Kaur552e2572019-03-08 11:00:29 +000020import android.annotation.FloatRange;
Nandana Dutt3386fb72018-12-12 17:26:57 +000021import android.annotation.IntDef;
22import android.annotation.NonNull;
23import android.annotation.Nullable;
24import android.annotation.RequiresPermission;
Nandana Dutt2083e8c2019-01-23 20:02:29 +000025import android.annotation.SystemApi;
Nandana Dutt3386fb72018-12-12 17:26:57 +000026import android.annotation.SystemService;
27import android.content.Context;
Nandana Dutt551906c2019-01-23 09:51:49 +000028
29import com.android.internal.util.Preconditions;
Nandana Dutt3386fb72018-12-12 17:26:57 +000030
31import java.io.FileDescriptor;
32import java.lang.annotation.Retention;
33import java.lang.annotation.RetentionPolicy;
Nandana Dutt28d8dd72019-01-25 14:31:05 +000034import java.util.concurrent.Executor;
Nandana Dutt3386fb72018-12-12 17:26:57 +000035
36/**
37 * Class that provides a privileged API to capture and consume bugreports.
38 *
39 * @hide
40 */
Nandana Dutt2083e8c2019-01-23 20:02:29 +000041@SystemApi
Nandana Dutt3386fb72018-12-12 17:26:57 +000042@SystemService(Context.BUGREPORT_SERVICE)
Abhijeet Kaur552e2572019-03-08 11:00:29 +000043public final class BugreportManager {
Nandana Dutt3386fb72018-12-12 17:26:57 +000044 private final Context mContext;
45 private final IDumpstate mBinder;
46
47 /** @hide */
48 public BugreportManager(@NonNull Context context, IDumpstate binder) {
49 mContext = context;
50 mBinder = binder;
51 }
52
53 /**
Nandana Dutt28d8dd72019-01-25 14:31:05 +000054 * An interface describing the callback for bugreport progress and status.
Nandana Dutt3386fb72018-12-12 17:26:57 +000055 */
Nandana Dutt28d8dd72019-01-25 14:31:05 +000056 public abstract static class BugreportCallback {
57 /** @hide */
58 @Retention(RetentionPolicy.SOURCE)
59 @IntDef(prefix = { "BUGREPORT_ERROR_" }, value = {
60 BUGREPORT_ERROR_INVALID_INPUT,
61 BUGREPORT_ERROR_RUNTIME,
62 BUGREPORT_ERROR_USER_DENIED_CONSENT,
Nandana Duttcfb3d482019-02-20 11:25:35 +000063 BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT,
64 BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS
Nandana Dutt28d8dd72019-01-25 14:31:05 +000065 })
66
67 /** Possible error codes taking a bugreport can encounter */
68 public @interface BugreportErrorCode {}
69
70 /** The input options were invalid */
71 public static final int BUGREPORT_ERROR_INVALID_INPUT =
72 IDumpstateListener.BUGREPORT_ERROR_INVALID_INPUT;
73
74 /** A runtime error occured */
75 public static final int BUGREPORT_ERROR_RUNTIME =
76 IDumpstateListener.BUGREPORT_ERROR_RUNTIME_ERROR;
77
78 /** User denied consent to share the bugreport */
79 public static final int BUGREPORT_ERROR_USER_DENIED_CONSENT =
80 IDumpstateListener.BUGREPORT_ERROR_USER_DENIED_CONSENT;
81
82 /** The request to get user consent timed out. */
83 public static final int BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT =
84 IDumpstateListener.BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT;
85
Nandana Duttcfb3d482019-02-20 11:25:35 +000086 /** There is currently a bugreport running. The caller should try again later. */
87 public static final int BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS =
88 IDumpstateListener.BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS;
89
Nandana Dutt3386fb72018-12-12 17:26:57 +000090 /**
91 * Called when there is a progress update.
92 * @param progress the progress in [0.0, 100.0]
93 */
Abhijeet Kaur552e2572019-03-08 11:00:29 +000094 public void onProgress(@FloatRange(from = 0f, to = 100f) float progress) {}
Nandana Duttbba7e822019-01-23 19:11:01 +000095
Nandana Dutt3386fb72018-12-12 17:26:57 +000096 /**
97 * Called when taking bugreport resulted in an error.
98 *
Nandana Duttbba7e822019-01-23 19:11:01 +000099 * <p>If {@code BUGREPORT_ERROR_USER_DENIED_CONSENT} is passed, then the user did not
100 * consent to sharing the bugreport with the calling app.
101 *
102 * <p>If {@code BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT} is passed, then the consent timed
103 * out, but the bugreport could be available in the internal directory of dumpstate for
104 * manual retrieval.
Nandana Duttcfb3d482019-02-20 11:25:35 +0000105 *
106 * <p> If {@code BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS} is passed, then the
107 * caller should try later, as only one bugreport can be in progress at a time.
Nandana Dutt3386fb72018-12-12 17:26:57 +0000108 */
Nandana Dutt28d8dd72019-01-25 14:31:05 +0000109 public void onError(@BugreportErrorCode int errorCode) {}
Nandana Dutt3386fb72018-12-12 17:26:57 +0000110
111 /**
Nandana Duttb2da22a2019-01-23 08:39:05 +0000112 * Called when taking bugreport finishes successfully.
Nandana Dutt3386fb72018-12-12 17:26:57 +0000113 */
Nandana Dutt28d8dd72019-01-25 14:31:05 +0000114 public void onFinished() {}
Nandana Dutt3386fb72018-12-12 17:26:57 +0000115 }
116
117 /**
Nandana Duttb2da22a2019-01-23 08:39:05 +0000118 * Starts a bugreport.
119 *
120 * <p>This starts a bugreport in the background. However the call itself can take several
Nandana Dutt28d8dd72019-01-25 14:31:05 +0000121 * seconds to return in the worst case. {@code callback} will receive progress and status
Nandana Duttb2da22a2019-01-23 08:39:05 +0000122 * updates.
123 *
124 * <p>The bugreport artifacts will be copied over to the given file descriptors only if the
125 * user consents to sharing with the calling app.
Nandana Dutt3386fb72018-12-12 17:26:57 +0000126 *
127 * @param bugreportFd file to write the bugreport. This should be opened in write-only,
128 * append mode.
129 * @param screenshotFd file to write the screenshot, if necessary. This should be opened
130 * in write-only, append mode.
131 * @param params options that specify what kind of a bugreport should be taken
Nandana Dutt28d8dd72019-01-25 14:31:05 +0000132 * @param callback callback for progress and status updates
Nandana Dutt3386fb72018-12-12 17:26:57 +0000133 */
134 @RequiresPermission(android.Manifest.permission.DUMP)
Nandana Dutt28d8dd72019-01-25 14:31:05 +0000135 public void startBugreport(@NonNull ParcelFileDescriptor bugreportFd,
136 @Nullable ParcelFileDescriptor screenshotFd,
137 @NonNull BugreportParams params,
138 @NonNull @CallbackExecutor Executor executor,
139 @NonNull BugreportCallback callback) {
Nandana Dutt551906c2019-01-23 09:51:49 +0000140 Preconditions.checkNotNull(bugreportFd);
141 Preconditions.checkNotNull(params);
142 Preconditions.checkNotNull(executor);
143 Preconditions.checkNotNull(callback);
Nandana Dutt28d8dd72019-01-25 14:31:05 +0000144 DumpstateListener dsListener = new DumpstateListener(executor, callback);
Nandana Dutt3386fb72018-12-12 17:26:57 +0000145 try {
Nandana Dutt161a4462019-01-16 18:18:38 +0000146 // Note: mBinder can get callingUid from the binder transaction.
147 mBinder.startBugreport(-1 /* callingUid */,
Nandana Dutt28d8dd72019-01-25 14:31:05 +0000148 mContext.getOpPackageName(),
Nandana Dutt551906c2019-01-23 09:51:49 +0000149 bugreportFd.getFileDescriptor(),
Nandana Dutt28d8dd72019-01-25 14:31:05 +0000150 (screenshotFd != null
151 ? screenshotFd.getFileDescriptor() : new FileDescriptor()),
Nandana Dutt161a4462019-01-16 18:18:38 +0000152 params.getMode(), dsListener);
Nandana Dutt3386fb72018-12-12 17:26:57 +0000153 } catch (RemoteException e) {
154 throw e.rethrowFromSystemServer();
155 }
156 }
157
Nandana Duttb2da22a2019-01-23 08:39:05 +0000158 /*
159 * Cancels a currently running bugreport.
160 */
161 @RequiresPermission(android.Manifest.permission.DUMP)
162 public void cancelBugreport() {
163 try {
164 mBinder.cancelBugreport();
165 } catch (RemoteException e) {
166 throw e.rethrowFromSystemServer();
167 }
168 }
169
Nandana Dutt551906c2019-01-23 09:51:49 +0000170 private final class DumpstateListener extends IDumpstateListener.Stub {
Nandana Dutt28d8dd72019-01-25 14:31:05 +0000171 private final Executor mExecutor;
172 private final BugreportCallback mCallback;
Nandana Dutt3386fb72018-12-12 17:26:57 +0000173
Nandana Dutt28d8dd72019-01-25 14:31:05 +0000174 DumpstateListener(Executor executor, @Nullable BugreportCallback callback) {
175 mExecutor = executor;
176 mCallback = callback;
Nandana Dutt3386fb72018-12-12 17:26:57 +0000177 }
178
179 @Override
Nandana Dutt432f8c72019-01-14 17:39:13 +0000180 public void onProgress(int progress) throws RemoteException {
Nandana Dutt28d8dd72019-01-25 14:31:05 +0000181 final long identity = Binder.clearCallingIdentity();
182 try {
183 mExecutor.execute(() -> {
184 mCallback.onProgress(progress);
185 });
186 } finally {
187 Binder.restoreCallingIdentity(identity);
188 }
Nandana Dutt432f8c72019-01-14 17:39:13 +0000189 }
190
191 @Override
192 public void onError(int errorCode) throws RemoteException {
Nandana Dutt28d8dd72019-01-25 14:31:05 +0000193 final long identity = Binder.clearCallingIdentity();
194 try {
195 mExecutor.execute(() -> {
196 mCallback.onError(errorCode);
197 });
198 } finally {
199 Binder.restoreCallingIdentity(identity);
200 }
Nandana Dutt432f8c72019-01-14 17:39:13 +0000201 }
202
203 @Override
Nandana Duttb2da22a2019-01-23 08:39:05 +0000204 public void onFinished() throws RemoteException {
Nandana Dutt28d8dd72019-01-25 14:31:05 +0000205 final long identity = Binder.clearCallingIdentity();
Nandana Duttb2da22a2019-01-23 08:39:05 +0000206 try {
Nandana Dutt28d8dd72019-01-25 14:31:05 +0000207 mExecutor.execute(() -> {
208 mCallback.onFinished();
209 });
Nandana Duttb2da22a2019-01-23 08:39:05 +0000210 } finally {
Nandana Dutt28d8dd72019-01-25 14:31:05 +0000211 Binder.restoreCallingIdentity(identity);
Nandana Duttb2da22a2019-01-23 08:39:05 +0000212 // The bugreport has finished. Let's shutdown the service to minimize its footprint.
213 cancelBugreport();
214 }
Nandana Dutt432f8c72019-01-14 17:39:13 +0000215 }
216
217 // Old methods; should go away
218 @Override
Nandana Dutt3386fb72018-12-12 17:26:57 +0000219 public void onProgressUpdated(int progress) throws RemoteException {
Nandana Duttc8c77bc2019-01-15 12:48:12 +0000220 // TODO(b/111441001): remove from interface
Nandana Dutt3386fb72018-12-12 17:26:57 +0000221 }
222
223 @Override
224 public void onMaxProgressUpdated(int maxProgress) throws RemoteException {
Nandana Duttc8c77bc2019-01-15 12:48:12 +0000225 // TODO(b/111441001): remove from interface
Nandana Dutt3386fb72018-12-12 17:26:57 +0000226 }
227
228 @Override
229 public void onSectionComplete(String title, int status, int size, int durationMs)
230 throws RemoteException {
Nandana Duttc8c77bc2019-01-15 12:48:12 +0000231 // TODO(b/111441001): remove from interface
Nandana Dutt3386fb72018-12-12 17:26:57 +0000232 }
233 }
234}