Skeleton API for IncidentManager to retrieve incident reports.
Bug: 123543706
Test: make
Change-Id: I0a41d476703cb0c1c728c6de1bf290162129e699
diff --git a/api/system-current.txt b/api/system-current.txt
index dcc3dc7..0f83ab3 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1298,6 +1298,7 @@
field public static final String ACTION_DEVICE_CUSTOMIZATION_READY = "android.intent.action.DEVICE_CUSTOMIZATION_READY";
field public static final String ACTION_FACTORY_RESET = "android.intent.action.FACTORY_RESET";
field public static final String ACTION_GLOBAL_BUTTON = "android.intent.action.GLOBAL_BUTTON";
+ field public static final String ACTION_INCIDENT_REPORT_READY = "android.intent.action.INCIDENT_REPORT_READY";
field public static final String ACTION_INSTALL_INSTANT_APP_PACKAGE = "android.intent.action.INSTALL_INSTANT_APP_PACKAGE";
field public static final String ACTION_INSTANT_APP_RESOLVER_SETTINGS = "android.intent.action.INSTANT_APP_RESOLVER_SETTINGS";
field public static final String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION";
@@ -5336,11 +5337,17 @@
public class IncidentManager {
method @RequiresPermission(android.Manifest.permission.APPROVE_INCIDENT_REPORTS) public void approveReport(android.net.Uri);
method @RequiresPermission("android.permission.REQUEST_INCIDENT_REPORT_APPROVAL") public void cancelAuthorization(android.os.IncidentManager.AuthListener);
+ method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void deleteIncidentReports(android.net.Uri);
method @RequiresPermission(android.Manifest.permission.APPROVE_INCIDENT_REPORTS) public void denyReport(android.net.Uri);
+ method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) @Nullable public android.os.IncidentManager.IncidentReport getIncidentReport(android.net.Uri);
+ method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) @NonNull public java.util.List<android.net.Uri> getIncidentReportList(String);
method @RequiresPermission(android.Manifest.permission.APPROVE_INCIDENT_REPORTS) public java.util.List<android.os.IncidentManager.PendingReport> getPendingReports();
method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void reportIncident(android.os.IncidentReportArgs);
method @RequiresPermission("android.permission.REQUEST_INCIDENT_REPORT_APPROVAL") public void requestAuthorization(int, String, int, android.os.IncidentManager.AuthListener);
field public static final int FLAG_CONFIRMATION_DIALOG = 1; // 0x1
+ field public static final int PRIVACY_POLICY_AUTO = 200; // 0xc8
+ field public static final int PRIVACY_POLICY_EXPLICIT = 100; // 0x64
+ field public static final int PRIVACY_POLICY_LOCAL = 0; // 0x0
}
public static class IncidentManager.AuthListener {
@@ -5349,6 +5356,17 @@
method public void onReportDenied();
}
+ public static class IncidentManager.IncidentReport implements java.io.Closeable android.os.Parcelable {
+ ctor public IncidentManager.IncidentReport(android.os.Parcel);
+ method public void close();
+ method public int describeContents();
+ method public java.io.InputStream getInputStream() throws java.io.IOException;
+ method public long getPrivacyPolicy();
+ method public long getTimestamp();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.os.IncidentManager.IncidentReport> CREATOR;
+ }
+
public static class IncidentManager.PendingReport {
ctor public IncidentManager.PendingReport(@NonNull android.net.Uri);
method public int getFlags();
diff --git a/api/test-current.txt b/api/test-current.txt
index 4ca3f56..2c0223d 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1284,11 +1284,17 @@
public class IncidentManager {
method @RequiresPermission(android.Manifest.permission.APPROVE_INCIDENT_REPORTS) public void approveReport(android.net.Uri);
method @RequiresPermission("android.permission.REQUEST_INCIDENT_REPORT_APPROVAL") public void cancelAuthorization(android.os.IncidentManager.AuthListener);
+ method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void deleteIncidentReports(android.net.Uri);
method @RequiresPermission(android.Manifest.permission.APPROVE_INCIDENT_REPORTS) public void denyReport(android.net.Uri);
+ method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) @Nullable public android.os.IncidentManager.IncidentReport getIncidentReport(android.net.Uri);
+ method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) @NonNull public java.util.List<android.net.Uri> getIncidentReportList(String);
method @RequiresPermission(android.Manifest.permission.APPROVE_INCIDENT_REPORTS) public java.util.List<android.os.IncidentManager.PendingReport> getPendingReports();
method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void reportIncident(android.os.IncidentReportArgs);
method @RequiresPermission("android.permission.REQUEST_INCIDENT_REPORT_APPROVAL") public void requestAuthorization(int, String, int, android.os.IncidentManager.AuthListener);
field public static final int FLAG_CONFIRMATION_DIALOG = 1; // 0x1
+ field public static final int PRIVACY_POLICY_AUTO = 200; // 0xc8
+ field public static final int PRIVACY_POLICY_EXPLICIT = 100; // 0x64
+ field public static final int PRIVACY_POLICY_LOCAL = 0; // 0x0
}
public static class IncidentManager.AuthListener {
@@ -1297,6 +1303,17 @@
method public void onReportDenied();
}
+ public static class IncidentManager.IncidentReport implements java.io.Closeable android.os.Parcelable {
+ ctor public IncidentManager.IncidentReport(android.os.Parcel);
+ method public void close();
+ method public int describeContents();
+ method public java.io.InputStream getInputStream() throws java.io.IOException;
+ method public long getPrivacyPolicy();
+ method public long getTimestamp();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.os.IncidentManager.IncidentReport> CREATOR;
+ }
+
public static class IncidentManager.PendingReport {
ctor public IncidentManager.PendingReport(@NonNull android.net.Uri);
method public int getFlags();
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 8d14091..65ea635 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1487,6 +1487,24 @@
"android.intent.action.PENDING_INCIDENT_REPORTS_CHANGED";
/**
+ * An incident report has been taken, and the user has approved it for sharing.
+ * <p>
+ * This will be sent directly to the registered receiver, which must have
+ * both the DUMP and USAGE_STATS permissions.
+ * <p>
+ * After receiving this, the application should wait until a suitable time
+ * (e.g. network available), get the list of available reports with
+ * {@link IncidentManager#getIncidentReportList IncidentManager.getIncidentReportList(String)}
+ * and then when the reports have been successfully uploaded, call
+ * {@link IncidentManager#deleteIncidentReport IncidentManager.deleteIncidentReport(Uri)}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String ACTION_INCIDENT_REPORT_READY =
+ "android.intent.action.INCIDENT_REPORT_READY";
+
+ /**
* Activity Action: Show power usage information to the user.
* <p>Input: Nothing.
* <p>Output: Nothing.
diff --git a/core/java/android/os/IncidentManager.java b/core/java/android/os/IncidentManager.java
index 88a578a..a1c7b08 100644
--- a/core/java/android/os/IncidentManager.java
+++ b/core/java/android/os/IncidentManager.java
@@ -16,7 +16,9 @@
package android.os;
+import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
@@ -25,6 +27,11 @@
import android.net.Uri;
import android.util.Slog;
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
@@ -95,6 +102,33 @@
*/
public static final int FLAG_CONFIRMATION_DIALOG = 0x1;
+ /**
+ * Flag marking fields and incident reports than can be taken
+ * off the device only via adb.
+ */
+ public static final int PRIVACY_POLICY_LOCAL = 0;
+
+ /**
+ * Flag marking fields and incident reports than can be taken
+ * off the device with contemporary consent.
+ */
+ public static final int PRIVACY_POLICY_EXPLICIT = 100;
+
+ /**
+ * Flag marking fields and incident reports than can be taken
+ * off the device with prior consent.
+ */
+ public static final int PRIVACY_POLICY_AUTO = 200;
+
+ /** @hide */
+ @IntDef(flag = false, prefix = { "PRIVACY_POLICY_" }, value = {
+ PRIVACY_POLICY_AUTO,
+ PRIVACY_POLICY_EXPLICIT,
+ PRIVACY_POLICY_LOCAL,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PrivacyPolicy {}
+
private final Context mContext;
private Object mLock = new Object();
@@ -203,6 +237,110 @@
}
/**
+ * Record of an incident report that has previously been taken.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static class IncidentReport implements Parcelable, Closeable {
+ private final long mTimestampMs;
+ private final int mPrivacyPolicy;
+ private ParcelFileDescriptor mFileDescriptor;
+
+ public IncidentReport(Parcel in) {
+ mTimestampMs = in.readLong();
+ mPrivacyPolicy = in.readInt();
+ if (in.readInt() != 0) {
+ mFileDescriptor = ParcelFileDescriptor.CREATOR.createFromParcel(in);
+ } else {
+ mFileDescriptor = null;
+ }
+ }
+
+ /**
+ * Close the input stream associated with this entry.
+ */
+ public void close() {
+ try {
+ if (mFileDescriptor != null) {
+ mFileDescriptor.close();
+ mFileDescriptor = null;
+ }
+ } catch (IOException e) {
+ }
+ }
+
+ /**
+ * Get the time at which this incident report was taken, in wall clock time
+ * ({@link System#uptimeMillis System.uptimeMillis()} time base).
+ */
+ public long getTimestamp() {
+ return mTimestampMs;
+ }
+
+ /**
+ * Get the privacy level to which this report has been filtered.
+ *
+ * @see #PRIVACY_POLICY_AUTO
+ * @see #PRIVACY_POLICY_EXPLICIT
+ * @see #PRIVACY_POLICY_LOCAL
+ */
+ public long getPrivacyPolicy() {
+ return mPrivacyPolicy;
+ }
+
+ /**
+ * Get the contents of this incident report.
+ */
+ public InputStream getInputStream() throws IOException {
+ if (mFileDescriptor == null) {
+ return null;
+ }
+ return new ParcelFileDescriptor.AutoCloseInputStream(mFileDescriptor);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public int describeContents() {
+ return mFileDescriptor != null ? Parcelable.CONTENTS_FILE_DESCRIPTOR : 0;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeLong(mTimestampMs);
+ out.writeInt(mPrivacyPolicy);
+ if (mFileDescriptor != null) {
+ out.writeInt(1);
+ mFileDescriptor.writeToParcel(out, flags);
+ } else {
+ out.writeInt(0);
+ }
+ }
+
+ /**
+ * {@link Parcelable.Creator Creator} for {@link IncidentReport}.
+ */
+ public static final Parcelable.Creator<IncidentReport> CREATOR = new Parcelable.Creator() {
+ /**
+ * @inheritDoc
+ */
+ public IncidentReport[] newArray(int size) {
+ return new IncidentReport[size];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public IncidentReport createFromParcel(Parcel in) {
+ return new IncidentReport(in);
+ }
+ };
+ }
+
+ /**
* Listener for the status of an incident report being authroized or denied.
*
* @see #requestAuthorization
@@ -242,7 +380,7 @@
}
/**
- * Take an incident report and put it in dropbox.
+ * Take an incident report.
*/
@RequiresPermission(allOf = {
android.Manifest.permission.DUMP,
@@ -325,6 +463,52 @@
}
}
+ /**
+ * Get the incident reports that are available for upload for the supplied
+ * broadcast recevier.
+ *
+ * @param receiverClass Class name of broadcast receiver in this package that
+ * was registered to retrieve reports.
+ *
+ * @return A list of {@link Uri Uris} that are awaiting upload.
+ */
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.DUMP,
+ android.Manifest.permission.PACKAGE_USAGE_STATS
+ })
+ public @NonNull List<Uri> getIncidentReportList(String receiverClass) {
+ throw new RuntimeException("implement me");
+ }
+
+ /**
+ * Get the incident report with the given URI id.
+ *
+ * @param uri Identifier of the incident report.
+ *
+ * @return an IncidentReport object, or null if the incident report has been
+ * expired from disk.
+ */
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.DUMP,
+ android.Manifest.permission.PACKAGE_USAGE_STATS
+ })
+ public @Nullable IncidentReport getIncidentReport(Uri uri) {
+ throw new RuntimeException("implement me");
+ }
+
+ /**
+ * Delete the incident report with the given URI id.
+ *
+ * @param uri Identifier of the incident report.
+ */
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.DUMP,
+ android.Manifest.permission.PACKAGE_USAGE_STATS
+ })
+ public void deleteIncidentReports(Uri uri) {
+ throw new RuntimeException("implement me");
+ }
+
private void reportIncidentInternal(IncidentReportArgs args) {
try {
final IIncidentManager service = getIIncidentManagerLocked();
diff --git a/core/java/android/os/IncidentReportArgs.java b/core/java/android/os/IncidentReportArgs.java
index 3ca7f77..8d44727 100644
--- a/core/java/android/os/IncidentReportArgs.java
+++ b/core/java/android/os/IncidentReportArgs.java
@@ -32,19 +32,16 @@
@TestApi
public final class IncidentReportArgs implements Parcelable {
- private static final int DEST_EXPLICIT = 100;
- private static final int DEST_AUTO = 200;
-
private final IntArray mSections = new IntArray();
private final ArrayList<byte[]> mHeaders = new ArrayList<byte[]>();
private boolean mAll;
- private int mDest;
+ private int mPrivacyPolicy;
/**
* Construct an incident report args with no fields.
*/
public IncidentReportArgs() {
- mDest = DEST_AUTO;
+ mPrivacyPolicy = IncidentManager.PRIVACY_POLICY_AUTO;
}
/**
@@ -75,7 +72,7 @@
out.writeByteArray(mHeaders.get(i));
}
- out.writeInt(mDest);
+ out.writeInt(mPrivacyPolicy);
}
public void readFromParcel(Parcel in) {
@@ -93,7 +90,7 @@
mHeaders.add(in.createByteArray());
}
- mDest = in.readInt();
+ mPrivacyPolicy = in.readInt();
}
public static final Parcelable.Creator<IncidentReportArgs> CREATOR
@@ -128,7 +125,7 @@
sb.append(", ");
sb.append(mHeaders.size());
sb.append(" headers), ");
- sb.append("Dest enum value: ").append(mDest);
+ sb.append("privacy: ").append(mPrivacyPolicy);
return sb.toString();
}
@@ -145,14 +142,15 @@
/**
* Set this incident report privacy policy spec.
*/
- public void setPrivacyPolicy(int dest) {
- switch (dest) {
- case DEST_EXPLICIT:
- case DEST_AUTO:
- mDest = dest;
+ public void setPrivacyPolicy(int privacyPolicy) {
+ switch (privacyPolicy) {
+ case IncidentManager.PRIVACY_POLICY_LOCAL:
+ case IncidentManager.PRIVACY_POLICY_EXPLICIT:
+ case IncidentManager.PRIVACY_POLICY_AUTO:
+ mPrivacyPolicy = privacyPolicy;
break;
default:
- mDest = DEST_AUTO;
+ mPrivacyPolicy = IncidentManager.PRIVACY_POLICY_AUTO;
}
}