org.robolectric.shadows.ShadowBugreportManager Maven / Gradle / Ivy
package org.robolectric.shadows;
import static android.os.Build.VERSION_CODES.Q;
import static android.os.Build.VERSION_CODES.R;
import android.os.BugreportManager;
import android.os.BugreportManager.BugreportCallback;
import android.os.BugreportParams;
import android.os.ParcelFileDescriptor;
import androidx.annotation.Nullable;
import java.io.IOException;
import java.util.concurrent.Executor;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
/**
* Implementation of {@link android.os.BugreportManager}.
*
* This class is not available in the public Android SDK, but it is available for system apps.
*/
@Implements(value = BugreportManager.class, minSdk = Q, isInAndroidSdk = false)
public class ShadowBugreportManager {
private boolean hasPermission = true;
@Nullable private ParcelFileDescriptor bugreportFd;
@Nullable private ParcelFileDescriptor screenshotFd;
@Nullable private Executor executor;
@Nullable private BugreportCallback callback;
private boolean bugreportRequested;
@Nullable private CharSequence shareTitle;
@Nullable private CharSequence shareDescription;
/**
* Starts a bugreport with which can execute callback methods on the provided executor.
*
*
If bugreport already in progress, {@link BugreportCallback#onError} will be executed.
*/
@Implementation
protected void startBugreport(
ParcelFileDescriptor bugreportFd,
ParcelFileDescriptor screenshotFd,
BugreportParams params,
Executor executor,
BugreportCallback callback) {
enforcePermission("startBugreport");
if (isBugreportInProgress()) {
executor.execute(
() -> callback.onError(BugreportCallback.BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS));
} else {
this.bugreportFd = bugreportFd;
this.screenshotFd = screenshotFd;
this.executor = executor;
this.callback = callback;
}
}
/**
* Normally requests the platform/system to take a bugreport and make the final bugreport
* available to the user.
*
*
This implementation just sets a boolean recording that the method was invoked, and the share
* title and description.
*/
@Implementation(minSdk = R)
protected void requestBugreport(
BugreportParams params, CharSequence shareTitle, CharSequence shareDescription) {
this.bugreportRequested = true;
this.shareTitle = shareTitle;
this.shareDescription = shareDescription;
}
/** Cancels bugreport in progress and executes {@link BugreportCallback#onError}. */
@Implementation
protected void cancelBugreport() {
enforcePermission("cancelBugreport");
executeOnError(BugreportCallback.BUGREPORT_ERROR_RUNTIME);
}
/** Executes {@link BugreportCallback#onProgress} on the provided Executor. */
public void executeOnProgress(float progress) {
if (isBugreportInProgress()) {
BugreportCallback callback = this.callback;
executor.execute(() -> callback.onProgress(progress));
}
}
/** Executes {@link BugreportCallback#onError} on the provided Executor. */
public void executeOnError(int errorCode) {
if (isBugreportInProgress()) {
BugreportCallback callback = this.callback;
executor.execute(() -> callback.onError(errorCode));
}
resetParams();
}
/** Executes {@link BugreportCallback#onFinished} on the provided Executor. */
public void executeOnFinished() {
if (isBugreportInProgress()) {
BugreportCallback callback = this.callback;
executor.execute(callback::onFinished);
}
resetParams();
}
public boolean isBugreportInProgress() {
return executor != null && callback != null;
}
/** Returns true if {@link #requestBugreport} was called. */
public boolean wasBugreportRequested() {
return bugreportRequested;
}
/**
* Simulates if the calling process has the required permissions to call BugreportManager methods.
*
*
If {@code hasPermission} is false, {@link #startBugreport} and {@link #cancelBugreport} will
* throw {@link SecurityException}.
*/
public void setHasPermission(boolean hasPermission) {
this.hasPermission = hasPermission;
}
private void enforcePermission(String message) {
if (!hasPermission) {
throw new SecurityException(message);
}
}
/** Returns the title of the bugreport if set with {@code requestBugreport}, else null. */
@Nullable
public CharSequence getShareTitle() {
return shareTitle;
}
/** Returns the description of the bugreport if set with {@code requestBugreport}, else null. */
@Nullable
public CharSequence getShareDescription() {
return shareDescription;
}
/** Returns the screenshot file descriptor if set with {@code startBugreport}, else null. */
@Nullable
public ParcelFileDescriptor getScreenshotFd() {
return screenshotFd;
}
private void resetParams() {
try {
bugreportFd.close();
if (screenshotFd != null) {
screenshotFd.close();
}
} catch (IOException e) {
// ignore.
}
bugreportFd = null;
screenshotFd = null;
executor = null;
callback = null;
bugreportRequested = false;
shareTitle = null;
shareDescription = null;
}
}