blob: 684369a6f720e642862e5eadc2ec1f63562e75da [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;
Nandana Dutt3386fb72018-12-12 17:26:57 +000020import android.annotation.IntDef;
21import android.annotation.NonNull;
22import android.annotation.Nullable;
23import android.annotation.RequiresPermission;
Nandana Dutt2083e8c2019-01-23 20:02:29 +000024import android.annotation.SystemApi;
Nandana Dutt3386fb72018-12-12 17:26:57 +000025import android.annotation.SystemService;
26import android.content.Context;
Nandana Dutt551906c2019-01-23 09:51:49 +000027
28import com.android.internal.util.Preconditions;
Nandana Dutt3386fb72018-12-12 17:26:57 +000029
30import java.io.FileDescriptor;
31import java.lang.annotation.Retention;
32import java.lang.annotation.RetentionPolicy;
Nandana Dutt28d8dd72019-01-25 14:31:05 +000033import java.util.concurrent.Executor;
Nandana Dutt3386fb72018-12-12 17:26:57 +000034
35/**
36 * Class that provides a privileged API to capture and consume bugreports.
37 *
38 * @hide
39 */
Nandana Dutt2083e8c2019-01-23 20:02:29 +000040@SystemApi
Nandana Dutt3386fb72018-12-12 17:26:57 +000041@SystemService(Context.BUGREPORT_SERVICE)
42public class BugreportManager {
43 private final Context mContext;
44 private final IDumpstate mBinder;
45
46 /** @hide */
47 public BugreportManager(@NonNull Context context, IDumpstate binder) {
48 mContext = context;
49 mBinder = binder;
50 }
51
52 /**
Nandana Dutt28d8dd72019-01-25 14:31:05 +000053 * An interface describing the callback for bugreport progress and status.
Nandana Dutt3386fb72018-12-12 17:26:57 +000054 */
Nandana Dutt28d8dd72019-01-25 14:31:05 +000055 public abstract static class BugreportCallback {
56 /** @hide */
57 @Retention(RetentionPolicy.SOURCE)
58 @IntDef(prefix = { "BUGREPORT_ERROR_" }, value = {
59 BUGREPORT_ERROR_INVALID_INPUT,
60 BUGREPORT_ERROR_RUNTIME,
61 BUGREPORT_ERROR_USER_DENIED_CONSENT,
Nandana Duttcfb3d482019-02-20 11:25:35 +000062 BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT,
63 BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS
Nandana Dutt28d8dd72019-01-25 14:31:05 +000064 })
65
66 /** Possible error codes taking a bugreport can encounter */
67 public @interface BugreportErrorCode {}
68
69 /** The input options were invalid */
70 public static final int BUGREPORT_ERROR_INVALID_INPUT =
71 IDumpstateListener.BUGREPORT_ERROR_INVALID_INPUT;
72
73 /** A runtime error occured */
74 public static final int BUGREPORT_ERROR_RUNTIME =
75 IDumpstateListener.BUGREPORT_ERROR_RUNTIME_ERROR;
76
77 /** User denied consent to share the bugreport */
78 public static final int BUGREPORT_ERROR_USER_DENIED_CONSENT =
79 IDumpstateListener.BUGREPORT_ERROR_USER_DENIED_CONSENT;
80
81 /** The request to get user consent timed out. */
82 public static final int BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT =
83 IDumpstateListener.BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT;
84
Nandana Duttcfb3d482019-02-20 11:25:35 +000085 /** There is currently a bugreport running. The caller should try again later. */
86 public static final int BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS =
87 IDumpstateListener.BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS;
88
Nandana Dutt3386fb72018-12-12 17:26:57 +000089 /**
90 * Called when there is a progress update.
91 * @param progress the progress in [0.0, 100.0]
92 */
Nandana Dutt28d8dd72019-01-25 14:31:05 +000093 public void onProgress(float progress) {}
Nandana Duttbba7e822019-01-23 19:11:01 +000094
Nandana Dutt3386fb72018-12-12 17:26:57 +000095 /**
96 * Called when taking bugreport resulted in an error.
97 *
Nandana Duttbba7e822019-01-23 19:11:01 +000098 * <p>If {@code BUGREPORT_ERROR_USER_DENIED_CONSENT} is passed, then the user did not
99 * consent to sharing the bugreport with the calling app.
100 *
101 * <p>If {@code BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT} is passed, then the consent timed
102 * out, but the bugreport could be available in the internal directory of dumpstate for
103 * manual retrieval.
Nandana Duttcfb3d482019-02-20 11:25:35 +0000104 *
105 * <p> If {@code BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS} is passed, then the
106 * caller should try later, as only one bugreport can be in progress at a time.
Nandana Dutt3386fb72018-12-12 17:26:57 +0000107 */
Nandana Dutt28d8dd72019-01-25 14:31:05 +0000108 public void onError(@BugreportErrorCode int errorCode) {}
Nandana Dutt3386fb72018-12-12 17:26:57 +0000109
110 /**
Nandana Duttb2da22a2019-01-23 08:39:05 +0000111 * Called when taking bugreport finishes successfully.
Nandana Dutt3386fb72018-12-12 17:26:57 +0000112 */
Nandana Dutt28d8dd72019-01-25 14:31:05 +0000113 public void onFinished() {}
Nandana Dutt3386fb72018-12-12 17:26:57 +0000114 }
115
116 /**
Nandana Duttb2da22a2019-01-23 08:39:05 +0000117 * Starts a bugreport.
118 *
119 * <p>This starts a bugreport in the background. However the call itself can take several
Nandana Dutt28d8dd72019-01-25 14:31:05 +0000120 * seconds to return in the worst case. {@code callback} will receive progress and status
Nandana Duttb2da22a2019-01-23 08:39:05 +0000121 * updates.
122 *
123 * <p>The bugreport artifacts will be copied over to the given file descriptors only if the
124 * user consents to sharing with the calling app.
Nandana Dutt3386fb72018-12-12 17:26:57 +0000125 *
126 * @param bugreportFd file to write the bugreport. This should be opened in write-only,
127 * append mode.
128 * @param screenshotFd file to write the screenshot, if necessary. This should be opened
129 * in write-only, append mode.
130 * @param params options that specify what kind of a bugreport should be taken
Nandana Dutt28d8dd72019-01-25 14:31:05 +0000131 * @param callback callback for progress and status updates
Nandana Dutt3386fb72018-12-12 17:26:57 +0000132 */
133 @RequiresPermission(android.Manifest.permission.DUMP)
Nandana Dutt28d8dd72019-01-25 14:31:05 +0000134 public void startBugreport(@NonNull ParcelFileDescriptor bugreportFd,
135 @Nullable ParcelFileDescriptor screenshotFd,
136 @NonNull BugreportParams params,
137 @NonNull @CallbackExecutor Executor executor,
138 @NonNull BugreportCallback callback) {
Nandana Dutt551906c2019-01-23 09:51:49 +0000139 Preconditions.checkNotNull(bugreportFd);
140 Preconditions.checkNotNull(params);
141 Preconditions.checkNotNull(executor);
142 Preconditions.checkNotNull(callback);
Nandana Dutt28d8dd72019-01-25 14:31:05 +0000143 DumpstateListener dsListener = new DumpstateListener(executor, callback);
Nandana Dutt3386fb72018-12-12 17:26:57 +0000144 try {
Nandana Dutt161a4462019-01-16 18:18:38 +0000145 // Note: mBinder can get callingUid from the binder transaction.
146 mBinder.startBugreport(-1 /* callingUid */,
Nandana Dutt28d8dd72019-01-25 14:31:05 +0000147 mContext.getOpPackageName(),
Nandana Dutt551906c2019-01-23 09:51:49 +0000148 bugreportFd.getFileDescriptor(),
Nandana Dutt28d8dd72019-01-25 14:31:05 +0000149 (screenshotFd != null
150 ? screenshotFd.getFileDescriptor() : new FileDescriptor()),
Nandana Dutt161a4462019-01-16 18:18:38 +0000151 params.getMode(), dsListener);
Nandana Dutt3386fb72018-12-12 17:26:57 +0000152 } catch (RemoteException e) {
153 throw e.rethrowFromSystemServer();
154 }
155 }
156
Nandana Duttb2da22a2019-01-23 08:39:05 +0000157 /*
158 * Cancels a currently running bugreport.
159 */
160 @RequiresPermission(android.Manifest.permission.DUMP)
161 public void cancelBugreport() {
162 try {
163 mBinder.cancelBugreport();
164 } catch (RemoteException e) {
165 throw e.rethrowFromSystemServer();
166 }
167 }
168
Nandana Dutt551906c2019-01-23 09:51:49 +0000169 private final class DumpstateListener extends IDumpstateListener.Stub {
Nandana Dutt28d8dd72019-01-25 14:31:05 +0000170 private final Executor mExecutor;
171 private final BugreportCallback mCallback;
Nandana Dutt3386fb72018-12-12 17:26:57 +0000172
Nandana Dutt28d8dd72019-01-25 14:31:05 +0000173 DumpstateListener(Executor executor, @Nullable BugreportCallback callback) {
174 mExecutor = executor;
175 mCallback = callback;
Nandana Dutt3386fb72018-12-12 17:26:57 +0000176 }
177
178 @Override
Nandana Dutt432f8c72019-01-14 17:39:13 +0000179 public void onProgress(int progress) throws RemoteException {
Nandana Dutt28d8dd72019-01-25 14:31:05 +0000180 final long identity = Binder.clearCallingIdentity();
181 try {
182 mExecutor.execute(() -> {
183 mCallback.onProgress(progress);
184 });
185 } finally {
186 Binder.restoreCallingIdentity(identity);
187 }
Nandana Dutt432f8c72019-01-14 17:39:13 +0000188 }
189
190 @Override
191 public void onError(int errorCode) throws RemoteException {
Nandana Dutt28d8dd72019-01-25 14:31:05 +0000192 final long identity = Binder.clearCallingIdentity();
193 try {
194 mExecutor.execute(() -> {
195 mCallback.onError(errorCode);
196 });
197 } finally {
198 Binder.restoreCallingIdentity(identity);
199 }
Nandana Dutt432f8c72019-01-14 17:39:13 +0000200 }
201
202 @Override
Nandana Duttb2da22a2019-01-23 08:39:05 +0000203 public void onFinished() throws RemoteException {
Nandana Dutt28d8dd72019-01-25 14:31:05 +0000204 final long identity = Binder.clearCallingIdentity();
Nandana Duttb2da22a2019-01-23 08:39:05 +0000205 try {
Nandana Dutt28d8dd72019-01-25 14:31:05 +0000206 mExecutor.execute(() -> {
207 mCallback.onFinished();
208 });
Nandana Duttb2da22a2019-01-23 08:39:05 +0000209 } finally {
Nandana Dutt28d8dd72019-01-25 14:31:05 +0000210 Binder.restoreCallingIdentity(identity);
Nandana Duttb2da22a2019-01-23 08:39:05 +0000211 // The bugreport has finished. Let's shutdown the service to minimize its footprint.
212 cancelBugreport();
213 }
Nandana Dutt432f8c72019-01-14 17:39:13 +0000214 }
215
216 // Old methods; should go away
217 @Override
Nandana Dutt3386fb72018-12-12 17:26:57 +0000218 public void onProgressUpdated(int progress) throws RemoteException {
Nandana Duttc8c77bc2019-01-15 12:48:12 +0000219 // TODO(b/111441001): remove from interface
Nandana Dutt3386fb72018-12-12 17:26:57 +0000220 }
221
222 @Override
223 public void onMaxProgressUpdated(int maxProgress) throws RemoteException {
Nandana Duttc8c77bc2019-01-15 12:48:12 +0000224 // TODO(b/111441001): remove from interface
Nandana Dutt3386fb72018-12-12 17:26:57 +0000225 }
226
227 @Override
228 public void onSectionComplete(String title, int status, int size, int durationMs)
229 throws RemoteException {
Nandana Duttc8c77bc2019-01-15 12:48:12 +0000230 // TODO(b/111441001): remove from interface
Nandana Dutt3386fb72018-12-12 17:26:57 +0000231 }
232 }
233}