Merge "Add a privileged API for capturing and consuming bugreports"
This commit is contained in:
@@ -109,6 +109,7 @@ import android.net.wifi.rtt.WifiRttManager;
|
||||
import android.nfc.NfcManager;
|
||||
import android.os.BatteryManager;
|
||||
import android.os.BatteryStats;
|
||||
import android.os.BugreportManager;
|
||||
import android.os.Build;
|
||||
import android.os.DeviceIdleManager;
|
||||
import android.os.DropBoxManager;
|
||||
@@ -116,6 +117,7 @@ import android.os.HardwarePropertiesManager;
|
||||
import android.os.IBatteryPropertiesRegistrar;
|
||||
import android.os.IBinder;
|
||||
import android.os.IDeviceIdleController;
|
||||
import android.os.IDumpstate;
|
||||
import android.os.IHardwarePropertiesManager;
|
||||
import android.os.IPowerManager;
|
||||
import android.os.IRecoverySystem;
|
||||
@@ -972,6 +974,16 @@ final class SystemServiceRegistry {
|
||||
return new IncidentManager(ctx);
|
||||
}});
|
||||
|
||||
registerService(Context.BUGREPORT_SERVICE, BugreportManager.class,
|
||||
new CachedServiceFetcher<BugreportManager>() {
|
||||
@Override
|
||||
public BugreportManager createService(ContextImpl ctx)
|
||||
throws ServiceNotFoundException {
|
||||
IBinder b = ServiceManager.getServiceOrThrow(Context.BUGREPORT_SERVICE);
|
||||
return new BugreportManager(ctx.getOuterContext(),
|
||||
IDumpstate.Stub.asInterface(b));
|
||||
}});
|
||||
|
||||
registerService(Context.AUTOFILL_MANAGER_SERVICE, AutofillManager.class,
|
||||
new CachedServiceFetcher<AutofillManager>() {
|
||||
@Override
|
||||
|
||||
@@ -4212,6 +4212,16 @@ public abstract class Context {
|
||||
@SystemApi
|
||||
public static final String STATS_MANAGER = "stats";
|
||||
|
||||
/**
|
||||
* Service to capture a bugreport.
|
||||
* @see #getSystemService(String)
|
||||
* @see android.os.BugreportManager
|
||||
* @hide
|
||||
*/
|
||||
// TODO: Expose API when the implementation is more complete.
|
||||
// @SystemApi
|
||||
public static final String BUGREPORT_SERVICE = "bugreport";
|
||||
|
||||
/**
|
||||
* Use with {@link #getSystemService(String)} to retrieve a {@link
|
||||
* android.content.om.OverlayManager} for managing overlay packages.
|
||||
|
||||
148
core/java/android/os/BugreportManager.java
Normal file
148
core/java/android/os/BugreportManager.java
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.os;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.RequiresPermission;
|
||||
import android.annotation.SystemService;
|
||||
import android.content.Context;
|
||||
import android.os.IBinder.DeathRecipient;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* Class that provides a privileged API to capture and consume bugreports.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
// TODO: Expose API when the implementation is more complete.
|
||||
// @SystemApi
|
||||
@SystemService(Context.BUGREPORT_SERVICE)
|
||||
public class BugreportManager {
|
||||
private final Context mContext;
|
||||
private final IDumpstate mBinder;
|
||||
|
||||
/** @hide */
|
||||
public BugreportManager(@NonNull Context context, IDumpstate binder) {
|
||||
mContext = context;
|
||||
mBinder = binder;
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface describing the listener for bugreport progress and status.
|
||||
*/
|
||||
public interface BugreportListener {
|
||||
/**
|
||||
* Called when there is a progress update.
|
||||
* @param progress the progress in [0.0, 100.0]
|
||||
*/
|
||||
void onProgress(float progress);
|
||||
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef(prefix = { "BUGREPORT_ERROR_" }, value = {
|
||||
BUGREPORT_ERROR_INVALID_INPUT,
|
||||
BUGREPORT_ERROR_RUNTIME
|
||||
})
|
||||
|
||||
/** Possible error codes taking a bugreport can encounter */
|
||||
@interface BugreportErrorCode {}
|
||||
|
||||
/** The input options were invalid */
|
||||
int BUGREPORT_ERROR_INVALID_INPUT = 1;
|
||||
|
||||
/** A runtime error occured */
|
||||
int BUGREPORT_ERROR_RUNTIME = 2;
|
||||
|
||||
/**
|
||||
* Called when taking bugreport resulted in an error.
|
||||
*
|
||||
* @param errorCode the error that occurred. Possible values are
|
||||
* {@code BUGREPORT_ERROR_INVALID_INPUT}, {@code BUGREPORT_ERROR_RUNTIME}.
|
||||
*/
|
||||
void onError(@BugreportErrorCode int errorCode);
|
||||
|
||||
/**
|
||||
* Called when taking bugreport finishes successfully
|
||||
*
|
||||
* @param durationMs time capturing bugreport took in milliseconds
|
||||
* @param title title for the bugreport; helpful in reminding the user why they took it
|
||||
* @param description detailed description for the bugreport
|
||||
*/
|
||||
void onFinished(long durationMs, @NonNull String title,
|
||||
@NonNull String description);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a bugreport asynchronously.
|
||||
*
|
||||
* @param bugreportFd file to write the bugreport. This should be opened in write-only,
|
||||
* append mode.
|
||||
* @param screenshotFd file to write the screenshot, if necessary. This should be opened
|
||||
* in write-only, append mode.
|
||||
* @param params options that specify what kind of a bugreport should be taken
|
||||
* @param listener callback for progress and status updates
|
||||
*/
|
||||
@RequiresPermission(android.Manifest.permission.DUMP)
|
||||
public void startBugreport(@NonNull FileDescriptor bugreportFd,
|
||||
@Nullable FileDescriptor screenshotFd,
|
||||
@NonNull BugreportParams params, @Nullable BugreportListener listener) {
|
||||
// TODO(b/111441001): Enforce android.Manifest.permission.DUMP if necessary.
|
||||
DumpstateListener dsListener = new DumpstateListener(listener);
|
||||
|
||||
try {
|
||||
mBinder.startBugreport(bugreportFd, screenshotFd, params.getMode(), dsListener);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO(b/111441001) Connect up with BugreportListener methods.
|
||||
private final class DumpstateListener extends IDumpstateListener.Stub
|
||||
implements DeathRecipient {
|
||||
private final BugreportListener mListener;
|
||||
|
||||
DumpstateListener(@Nullable BugreportListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void binderDied() {
|
||||
// TODO(b/111441001): implement
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgressUpdated(int progress) throws RemoteException {
|
||||
// TODO(b/111441001): implement
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMaxProgressUpdated(int maxProgress) throws RemoteException {
|
||||
// TODO(b/111441001): implement
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSectionComplete(String title, int status, int size, int durationMs)
|
||||
throws RemoteException {
|
||||
// TODO(b/111441001): implement
|
||||
}
|
||||
}
|
||||
}
|
||||
90
core/java/android/os/BugreportParams.java
Normal file
90
core/java/android/os/BugreportParams.java
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.os;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* Parameters that specify what kind of bugreport should be taken.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
// TODO: Expose API when the implementation is more complete.
|
||||
// @SystemApi
|
||||
public final class BugreportParams {
|
||||
private final int mMode;
|
||||
|
||||
public BugreportParams(@BugreportMode int mode) {
|
||||
mMode = mode;
|
||||
}
|
||||
|
||||
public int getMode() {
|
||||
return mMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines acceptable types of bugreports.
|
||||
*/
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef(prefix = { "BUGREPORT_MODE_" }, value = {
|
||||
BUGREPORT_MODE_FULL,
|
||||
BUGREPORT_MODE_INTERACTIVE,
|
||||
BUGREPORT_MODE_REMOTE,
|
||||
BUGREPORT_MODE_WEAR,
|
||||
BUGREPORT_MODE_TELEPHONY,
|
||||
BUGREPORT_MODE_WIFI
|
||||
})
|
||||
public @interface BugreportMode {}
|
||||
|
||||
/**
|
||||
* Options for a bugreport without user interference (and hence causing less
|
||||
* interference to the system), but includes all sections.
|
||||
*/
|
||||
public static final int BUGREPORT_MODE_FULL = IDumpstate.BUGREPORT_MODE_FULL;
|
||||
|
||||
/**
|
||||
* Options that allow user to monitor progress and enter additional data; might not
|
||||
* include all sections.
|
||||
*/
|
||||
public static final int BUGREPORT_MODE_INTERACTIVE = IDumpstate.BUGREPORT_MODE_INTERACTIVE;
|
||||
|
||||
/**
|
||||
* Options for a bugreport requested remotely by administrator of the Device Owner app,
|
||||
* not the device's user.
|
||||
*/
|
||||
public static final int BUGREPORT_MODE_REMOTE = IDumpstate.BUGREPORT_MODE_REMOTE;
|
||||
|
||||
/**
|
||||
* Options for a bugreport on a wearable device.
|
||||
*/
|
||||
public static final int BUGREPORT_MODE_WEAR = IDumpstate.BUGREPORT_MODE_WEAR;
|
||||
|
||||
/**
|
||||
* Options for a lightweight version of bugreport that only includes a few, urgent
|
||||
* sections used to report telephony bugs.
|
||||
*/
|
||||
public static final int BUGREPORT_MODE_TELEPHONY = IDumpstate.BUGREPORT_MODE_TELEPHONY;
|
||||
|
||||
/**
|
||||
* Options for a lightweight bugreport that only includes a few sections related to
|
||||
* Wifi.
|
||||
*/
|
||||
public static final int BUGREPORT_MODE_WIFI = IDumpstate.BUGREPORT_MODE_WIFI;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.os;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.android.server.SystemService;
|
||||
|
||||
/**
|
||||
* Service that provides a privileged API to capture and consume bugreports.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class BugreportManagerService extends SystemService {
|
||||
private static final String TAG = "BugreportManagerService";
|
||||
|
||||
private BugreportManagerServiceImpl mService;
|
||||
|
||||
public BugreportManagerService(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
mService = new BugreportManagerServiceImpl(getContext());
|
||||
// TODO(b/111441001): Needs sepolicy to be submitted first.
|
||||
// publishBinderService(Context.BUGREPORT_SERVICE, mService);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.os;
|
||||
|
||||
import android.annotation.RequiresPermission;
|
||||
import android.content.Context;
|
||||
import android.os.BugreportParams;
|
||||
import android.os.IDumpstate;
|
||||
import android.os.IDumpstateListener;
|
||||
import android.os.IDumpstateToken;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.SystemClock;
|
||||
import android.os.SystemProperties;
|
||||
import android.util.Slog;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
|
||||
// TODO(b/111441001):
|
||||
// 1. Handle the case where another bugreport is in progress
|
||||
// 2. Make everything threadsafe
|
||||
// 3. Pass validation & other errors on listener
|
||||
|
||||
/**
|
||||
* Implementation of the service that provides a privileged API to capture and consume bugreports.
|
||||
*
|
||||
* <p>Delegates the actualy generation to a native implementation of {@code Dumpstate}.
|
||||
*/
|
||||
class BugreportManagerServiceImpl extends IDumpstate.Stub {
|
||||
private static final String TAG = "BugreportManagerService";
|
||||
private static final long DEFAULT_BUGREPORT_SERVICE_TIMEOUT_MILLIS = 30 * 1000;
|
||||
|
||||
private IDumpstate mDs = null;
|
||||
private final Context mContext;
|
||||
|
||||
BugreportManagerServiceImpl(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
@RequiresPermission(android.Manifest.permission.DUMP)
|
||||
public IDumpstateToken setListener(String name, IDumpstateListener listener,
|
||||
boolean getSectionDetails) throws RemoteException {
|
||||
// TODO(b/111441001): Figure out if lazy setting of listener should be allowed
|
||||
// and if so how to handle it.
|
||||
throw new UnsupportedOperationException("setListener is not allowed on this service");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@RequiresPermission(android.Manifest.permission.DUMP)
|
||||
public void startBugreport(FileDescriptor bugreportFd, FileDescriptor screenshotFd,
|
||||
int bugreportMode, IDumpstateListener listener) throws RemoteException {
|
||||
|
||||
validate(bugreportMode);
|
||||
|
||||
mDs = getDumpstateService();
|
||||
if (mDs == null) {
|
||||
Slog.w(TAG, "Unable to get bugreport service");
|
||||
// TODO(b/111441001): pass error on listener
|
||||
return;
|
||||
}
|
||||
mDs.startBugreport(bugreportFd, screenshotFd, bugreportMode, listener);
|
||||
}
|
||||
|
||||
private boolean validate(@BugreportParams.BugreportMode int mode) {
|
||||
if (mode != BugreportParams.BUGREPORT_MODE_FULL
|
||||
&& mode != BugreportParams.BUGREPORT_MODE_INTERACTIVE
|
||||
&& mode != BugreportParams.BUGREPORT_MODE_REMOTE
|
||||
&& mode != BugreportParams.BUGREPORT_MODE_WEAR
|
||||
&& mode != BugreportParams.BUGREPORT_MODE_TELEPHONY
|
||||
&& mode != BugreportParams.BUGREPORT_MODE_WIFI) {
|
||||
Slog.w(TAG, "Unknown bugreport mode: " + mode);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Start and get a handle to the native implementation of {@code IDumpstate} which does the
|
||||
* actual bugreport generation.
|
||||
*
|
||||
* <p>Generating bugreports requires root privileges. To limit the footprint
|
||||
* of the root access, the actual generation in Dumpstate binary is accessed as a
|
||||
* oneshot service 'bugreport'.
|
||||
*/
|
||||
private IDumpstate getDumpstateService() {
|
||||
// Start bugreport service.
|
||||
SystemProperties.set("ctl.start", "bugreport");
|
||||
|
||||
IDumpstate ds = null;
|
||||
boolean timedOut = false;
|
||||
int totalTimeWaitedMillis = 0;
|
||||
int seedWaitTimeMillis = 500;
|
||||
while (!timedOut) {
|
||||
// Note that the binder service on the native side is "dumpstate".
|
||||
ds = IDumpstate.Stub.asInterface(ServiceManager.getService("dumpstate"));
|
||||
if (ds != null) {
|
||||
Slog.i(TAG, "Got bugreport service handle.");
|
||||
break;
|
||||
}
|
||||
SystemClock.sleep(seedWaitTimeMillis);
|
||||
Slog.i(TAG,
|
||||
"Waiting to get dumpstate service handle (" + totalTimeWaitedMillis + "ms)");
|
||||
totalTimeWaitedMillis += seedWaitTimeMillis;
|
||||
seedWaitTimeMillis *= 2;
|
||||
timedOut = totalTimeWaitedMillis > DEFAULT_BUGREPORT_SERVICE_TIMEOUT_MILLIS;
|
||||
}
|
||||
if (timedOut) {
|
||||
Slog.w(TAG,
|
||||
"Timed out waiting to get dumpstate service handle ("
|
||||
+ totalTimeWaitedMillis + "ms)");
|
||||
}
|
||||
return ds;
|
||||
}
|
||||
}
|
||||
@@ -101,6 +101,7 @@ import com.android.server.net.watchlist.NetworkWatchlistService;
|
||||
import com.android.server.notification.NotificationManagerService;
|
||||
import com.android.server.oemlock.OemLockService;
|
||||
import com.android.server.om.OverlayManagerService;
|
||||
import com.android.server.os.BugreportManagerService;
|
||||
import com.android.server.os.DeviceIdentifiersPolicyService;
|
||||
import com.android.server.os.SchedulingPolicyService;
|
||||
import com.android.server.pm.BackgroundDexOptService;
|
||||
@@ -742,6 +743,11 @@ public final class SystemServer {
|
||||
traceBeginAndSlog("StartBinderCallsStatsService");
|
||||
BinderCallsStatsService.start();
|
||||
traceEnd();
|
||||
|
||||
// Service to capture bugreports.
|
||||
traceBeginAndSlog("StartBugreportManagerService");
|
||||
mSystemServiceManager.startService(BugreportManagerService.class);
|
||||
traceEnd();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user