Public API: Incident report section registration
Mark incident report section registration and unregistration as public
APIs, and protect them with
android.permission.REGISTER_INCIDENT_REPORT_SECTION.
Bug: 144789854
Test: Build
Change-Id: I0d79ca12054292412e10a63ba2e6f45a149066a9
diff --git a/api/system-current.txt b/api/system-current.txt
index cb4c4b7..b3ba230 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -7326,8 +7326,11 @@
method @Nullable @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public android.os.IncidentManager.IncidentReport getIncidentReport(android.net.Uri);
method @NonNull @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) 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 public void registerSection(int, @NonNull String, @NonNull android.os.IncidentManager.DumpCallback);
+ method public void registerSection(int, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.IncidentManager.DumpCallback);
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);
+ method public void unregisterSection(int);
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
@@ -7340,6 +7343,11 @@
method public void onReportDenied();
}
+ public static class IncidentManager.DumpCallback {
+ ctor public IncidentManager.DumpCallback();
+ method public void onDumpSection(int, @NonNull java.io.OutputStream);
+ }
+
public static class IncidentManager.IncidentReport implements java.io.Closeable android.os.Parcelable {
ctor public IncidentManager.IncidentReport(android.os.Parcel);
method public void close();
diff --git a/api/test-current.txt b/api/test-current.txt
index 190f9fe..4a86b8b 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -2033,8 +2033,11 @@
method @Nullable @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public android.os.IncidentManager.IncidentReport getIncidentReport(android.net.Uri);
method @NonNull @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) 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 public void registerSection(int, @NonNull String, @NonNull android.os.IncidentManager.DumpCallback);
+ method public void registerSection(int, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.IncidentManager.DumpCallback);
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);
+ method public void unregisterSection(int);
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
@@ -2047,6 +2050,11 @@
method public void onReportDenied();
}
+ public static class IncidentManager.DumpCallback {
+ ctor public IncidentManager.DumpCallback();
+ method public void onDumpSection(int, @NonNull java.io.OutputStream);
+ }
+
public static class IncidentManager.IncidentReport implements java.io.Closeable android.os.Parcelable {
ctor public IncidentManager.IncidentReport(android.os.Parcel);
method public void close();
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp
index 62312d1..6c2b855 100644
--- a/cmds/incidentd/src/IncidentService.cpp
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -350,11 +350,11 @@
Status IncidentService::registerSection(const int id, const String16& name16,
const sp<IIncidentDumpCallback>& callback) {
const char* name = String8(name16).c_str();
- ALOGI("Register section %d: %s", id, name);
+ const uid_t callingUid = IPCThreadState::self()->getCallingUid();
+ ALOGI("Uid %d registers section %d '%s'", callingUid, id, name);
if (callback == nullptr) {
return Status::fromExceptionCode(Status::EX_NULL_POINTER);
}
- const uid_t callingUid = IPCThreadState::self()->getCallingUid();
for (int i = 0; i < mRegisteredSections.size(); i++) {
if (mRegisteredSections.at(i)->id == id) {
if (mRegisteredSections.at(i)->uid != callingUid) {
@@ -370,8 +370,9 @@
}
Status IncidentService::unregisterSection(const int id) {
- ALOGI("Unregister section %d", id);
uid_t callingUid = IPCThreadState::self()->getCallingUid();
+ ALOGI("Uid %d unregisters section %d", callingUid, id);
+
for (auto it = mRegisteredSections.begin(); it != mRegisteredSections.end(); it++) {
if ((*it)->id == id) {
if ((*it)->uid != callingUid) {
diff --git a/core/java/android/os/IncidentManager.java b/core/java/android/os/IncidentManager.java
index f6563eb..8dde117 100644
--- a/core/java/android/os/IncidentManager.java
+++ b/core/java/android/os/IncidentManager.java
@@ -36,6 +36,7 @@
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.Executor;
/**
@@ -426,10 +427,9 @@
*
* @see #registerSection
* @see #unregisterSection
- *
- * @hide
*/
public static class DumpCallback {
+ private int mId;
private Executor mExecutor;
IIncidentDumpCallback.Stub mBinder = new IIncidentDumpCallback.Stub() {
@@ -437,20 +437,25 @@
public void onDumpSection(ParcelFileDescriptor pfd) {
if (mExecutor != null) {
mExecutor.execute(() -> {
- DumpCallback.this.onDumpSection(
+ DumpCallback.this.onDumpSection(mId,
new ParcelFileDescriptor.AutoCloseOutputStream(pfd));
});
} else {
- DumpCallback.this.onDumpSection(
+ DumpCallback.this.onDumpSection(mId,
new ParcelFileDescriptor.AutoCloseOutputStream(pfd));
}
}
};
/**
- * Called when incidentd requests to dump this section.
+ * Dump the registered section as a protobuf message to the given OutputStream. Called when
+ * incidentd requests to dump this section.
+ *
+ * @param id the id of the registered section. The same id used in calling
+ * {@link #registerSection(int, String, DumpCallback)} will be passed in here.
+ * @param out the OutputStream to write the protobuf message
*/
- public void onDumpSection(OutputStream out) {
+ public void onDumpSection(int id, @NonNull OutputStream out) {
}
}
@@ -563,12 +568,20 @@
/**
* Register a callback to dump an extended incident report section with the given id and name.
- * The callback function will be invoked when an incident report with all sections or sections
- * matching the given id is being taken.
*
- * @hide
+ * Calling <code>registerSection</code> with a duplicate id will override previous registration.
+ * However, the request must come from the same calling uid.
+ *
+ * @param id the ID of the extended section. It should be unique system-wide, and be
+ * different from IDs of all existing section in
+ * frameworks/base/core/proto/android/os/incident.proto.
+ * Also see incident.proto for other rules about the ID.
+ * @param name the name to display in logs and/or stderr when taking an incident report
+ * containing this section, mainly for debugging purpose
+ * @param callback the callback function to be invoked when an incident report with all sections
+ * or sections matching the given id is being taken
*/
- public void registerSection(int id, String name, @NonNull DumpCallback callback) {
+ public void registerSection(int id, @NonNull String name, @NonNull DumpCallback callback) {
registerSection(id, name, mContext.getMainExecutor(), callback);
}
@@ -576,16 +589,27 @@
* Register a callback to dump an extended incident report section with the given id and name,
* running on the supplied executor.
*
- * @hide
+ * @param id the ID of the extended section. It should be unique system-wide, and be
+ * different from IDs of all existing section in
+ * frameworks/base/core/proto/android/os/incident.proto.
+ * Also see incident.proto for other rules about the ID.
+ * @param name the name to display in logs and/or stderr when taking an incident report
+ * containing this section, mainly for debugging purpose
+ * @param executor the executor used to run the callback
+ * @param callback the callback function to be invoked when an incident report with all sections
+ * or sections matching the given id is being taken
*/
- public void registerSection(int id, String name, @NonNull @CallbackExecutor Executor executor,
- @NonNull DumpCallback callback) {
+ public void registerSection(int id, @NonNull String name,
+ @NonNull @CallbackExecutor Executor executor, @NonNull DumpCallback callback) {
+ Objects.requireNonNull(executor, "executor cannot be null");
+ Objects.requireNonNull(callback, "callback cannot be null");
try {
if (callback.mExecutor != null) {
throw new RuntimeException("Do not reuse DumpCallback objects when calling"
+ " registerSection");
}
callback.mExecutor = executor;
+ callback.mId = id;
final IIncidentManager service = getIIncidentManagerLocked();
if (service == null) {
Slog.e(TAG, "registerSection can't find incident binder service");
@@ -599,9 +623,7 @@
/**
* Unregister an extended section dump function. The section must be previously registered with
- * {@link #registerSection(int, String, DumpCallback)}
- *
- * @hide
+ * {@link #registerSection(int, String, DumpCallback)} by the same calling uid.
*/
public void unregisterSection(int id) {
try {
@@ -719,7 +741,7 @@
throw new RuntimeException("Invalid URI: No "
+ URI_PARAM_REPORT_ID + " parameter. " + uri);
}
-
+
try {
getCompanionServiceLocked().deleteIncidentReports(pkg, cls, id);
} catch (RemoteException ex) {