OMS: include idmap data in dump
Teach the overlay manager to ask the idmap service to pretty print the
contents of each idmap file as part of OMS dump. This creates a single
entry point for dumping both OMS and idmap data, and circumvents the
problem of accessing the idmap service if it has been killed due to
inactivity.
Example idmap section:
---- 8< ----
IDMAP OF com.android.theme.color.sand
Paths:
target path : /system/framework/framework-res.apk
overlay path : /product/overlay/AccentColorSand/AccentColorSandOverlay.apk
Debug info:
W failed to find resource 'string/accent_color_overlay'
Mapping:
0x0106006e -> 0x7f010000 (color/accent_device_default_dark -> color/accent_device_default_dark)
0x01060070 -> 0x7f010001 (color/accent_device_default_light -> color/accent_device_default_light)
---- >8 ----
Bug: 189963636
Test: adb exec-out dumpsys
Test: adb exec-out cmd overlay dump
Test: adb exec-out cmd overlay dump <overlay-identifier>
Change-Id: I9de6ba646ad4714c9d0f0d8081fbf632577107e7
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp
index 4f775aa..73a7240 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.cpp
+++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp
@@ -33,6 +33,7 @@
#include "idmap2/BinaryStreamVisitor.h"
#include "idmap2/FileUtils.h"
#include "idmap2/Idmap.h"
+#include "idmap2/PrettyPrintVisitor.h"
#include "idmap2/Result.h"
#include "idmap2/SysTrace.h"
@@ -45,6 +46,7 @@
using android::idmap2::Idmap;
using android::idmap2::IdmapHeader;
using android::idmap2::OverlayResourceContainer;
+using android::idmap2::PrettyPrintVisitor;
using android::idmap2::TargetResourceContainer;
using android::idmap2::utils::kIdmapCacheDir;
using android::idmap2::utils::kIdmapFilePermissionMask;
@@ -352,4 +354,24 @@
return ok();
}
+binder::Status Idmap2Service::dumpIdmap(const std::string& overlay_path,
+ std::string* _aidl_return) {
+ assert(_aidl_return);
+
+ const auto idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_path);
+ std::ifstream fin(idmap_path);
+ const auto idmap = Idmap::FromBinaryStream(fin);
+ fin.close();
+ if (!idmap) {
+ return error(idmap.GetErrorMessage());
+ }
+
+ std::stringstream stream;
+ PrettyPrintVisitor visitor(stream);
+ (*idmap)->accept(&visitor);
+ *_aidl_return = stream.str();
+
+ return ok();
+}
+
} // namespace android::os
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.h b/cmds/idmap2/idmap2d/Idmap2Service.h
index 4d16ff3..40843f7 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.h
+++ b/cmds/idmap2/idmap2d/Idmap2Service.h
@@ -60,6 +60,8 @@
binder::Status getFabricatedOverlayInfos(
std::vector<os::FabricatedOverlayInfo>* _aidl_return) override;
+ binder::Status dumpIdmap(const std::string& overlay_path, std::string* _aidl_return) override;
+
private:
// idmap2d is killed after a period of inactivity, so any information stored on this class should
// be able to be recalculated if idmap2 dies and restarts.
diff --git a/cmds/idmap2/idmap2d/aidl/services/android/os/IIdmap2.aidl b/cmds/idmap2/idmap2d/aidl/services/android/os/IIdmap2.aidl
index 35bca98..48cee69 100644
--- a/cmds/idmap2/idmap2d/aidl/services/android/os/IIdmap2.aidl
+++ b/cmds/idmap2/idmap2d/aidl/services/android/os/IIdmap2.aidl
@@ -16,8 +16,8 @@
package android.os;
-import android.os.FabricatedOverlayInternal;
import android.os.FabricatedOverlayInfo;
+import android.os.FabricatedOverlayInternal;
/**
* @hide
@@ -40,4 +40,5 @@
@nullable FabricatedOverlayInfo createFabricatedOverlay(in FabricatedOverlayInternal overlay);
List<FabricatedOverlayInfo> getFabricatedOverlayInfos();
boolean deleteFabricatedOverlay(@utf8InCpp String path);
+ @utf8InCpp String dumpIdmap(@utf8InCpp String overlayApkPath);
}
diff --git a/services/core/java/com/android/server/om/IdmapDaemon.java b/services/core/java/com/android/server/om/IdmapDaemon.java
index 2ebc8ed..f2b6b1d 100644
--- a/services/core/java/com/android/server/om/IdmapDaemon.java
+++ b/services/core/java/com/android/server/om/IdmapDaemon.java
@@ -170,6 +170,16 @@
}
}
+ String dumpIdmap(@NonNull String overlayPath) {
+ try (Connection c = connect()) {
+ String dump = mService.dumpIdmap(overlayPath);
+ return TextUtils.nullIfEmpty(dump);
+ } catch (Exception e) {
+ Slog.wtf(TAG, "failed to dump idmap", e);
+ return null;
+ }
+ }
+
private IBinder getIdmapService() throws TimeoutException, RemoteException {
SystemService.start(IDMAP_DAEMON);
diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java
index 64362c9..157a1fc 100644
--- a/services/core/java/com/android/server/om/IdmapManager.java
+++ b/services/core/java/com/android/server/om/IdmapManager.java
@@ -22,7 +22,6 @@
import android.annotation.NonNull;
import android.content.om.OverlayInfo;
import android.content.om.OverlayableInfo;
-import android.content.pm.PackageParser;
import android.os.Build.VERSION_CODES;
import android.os.FabricatedOverlayInfo;
import android.os.FabricatedOverlayInternal;
@@ -142,6 +141,14 @@
}
/**
+ * Gets the idmap data associated with an overlay, in dump format.
+ * Only indented for debugging.
+ */
+ String dumpIdmap(@NonNull String overlayPath) {
+ return mIdmapDaemon.dumpIdmap(overlayPath);
+ }
+
+ /**
* Checks if overlayable and policies should be enforced on the specified overlay for backwards
* compatibility with pre-Q overlays.
*/
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index 799ab46..e4ca5b6 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -22,6 +22,7 @@
import static android.content.om.OverlayInfo.STATE_NO_IDMAP;
import static android.content.om.OverlayInfo.STATE_OVERLAY_IS_BEING_REPLACED;
import static android.content.om.OverlayInfo.STATE_TARGET_IS_BEING_REPLACED;
+import static android.os.UserHandle.USER_SYSTEM;
import static com.android.server.om.OverlayManagerService.DEBUG;
import static com.android.server.om.OverlayManagerService.TAG;
@@ -38,6 +39,7 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Pair;
import android.util.Slog;
import com.android.internal.content.om.OverlayConfig;
@@ -682,8 +684,38 @@
}
void dump(@NonNull final PrintWriter pw, @NonNull DumpState dumpState) {
+ Pair<OverlayIdentifier, String> overlayIdmap = null;
+ if (dumpState.getPackageName() != null) {
+ OverlayIdentifier id = new OverlayIdentifier(dumpState.getPackageName(),
+ dumpState.getOverlayName());
+ OverlayInfo oi = mSettings.getNullableOverlayInfo(id, USER_SYSTEM);
+ if (oi != null) {
+ overlayIdmap = new Pair<>(id, oi.baseCodePath);
+ }
+ }
+
+ // settings
mSettings.dump(pw, dumpState);
- if (dumpState.getPackageName() == null) {
+
+ // idmap data
+ if (dumpState.getField() == null) {
+ Set<Pair<OverlayIdentifier, String>> allIdmaps = (overlayIdmap != null)
+ ? Set.of(overlayIdmap) : mSettings.getAllIdentifiersAndBaseCodePaths();
+ for (Pair<OverlayIdentifier, String> pair : allIdmaps) {
+ pw.println("IDMAP OF " + pair.first);
+ String dump = mIdmapManager.dumpIdmap(pair.second);
+ if (dump != null) {
+ pw.println(dump);
+ } else {
+ OverlayInfo oi = mSettings.getNullableOverlayInfo(pair.first, USER_SYSTEM);
+ pw.println((oi != null && !mIdmapManager.idmapExists(oi))
+ ? "<missing idmap>" : "<internal error>");
+ }
+ }
+ }
+
+ // default overlays
+ if (overlayIdmap == null) {
pw.println("Default overlays: " + TextUtils.join(";", mDefaultOverlays));
}
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerSettings.java b/services/core/java/com/android/server/om/OverlayManagerSettings.java
index e3e0906..55bca17 100644
--- a/services/core/java/com/android/server/om/OverlayManagerSettings.java
+++ b/services/core/java/com/android/server/om/OverlayManagerSettings.java
@@ -26,6 +26,7 @@
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Pair;
import android.util.Slog;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
@@ -205,6 +206,12 @@
return paths;
}
+ Set<Pair<OverlayIdentifier, String>> getAllIdentifiersAndBaseCodePaths() {
+ final Set<Pair<OverlayIdentifier, String>> set = new ArraySet<>();
+ mItems.forEach(item -> set.add(new Pair(item.mOverlay, item.mBaseCodePath)));
+ return set;
+ }
+
@NonNull
List<OverlayInfo> removeIf(@NonNull final Predicate<OverlayInfo> predicate, final int userId) {
return removeIf(info -> (predicate.test(info) && info.userId == userId));