Merge "Add a privileged API for capturing and consuming bugreports"

This commit is contained in:
Nandana Dutt
2019-01-11 15:21:21 +00:00
committed by Gerrit Code Review
7 changed files with 439 additions and 0 deletions

View File

@@ -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

View File

@@ -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.

View 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
}
}
}

View 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;
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
/**