summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/Android.bp5
-rw-r--r--core/java/android/app/SystemServiceRegistry.java13
-rw-r--r--core/java/android/content/Context.java10
-rw-r--r--core/java/android/transparency/BinaryTransparencyManager.java83
-rw-r--r--core/java/android/transparency/OWNERS4
-rw-r--r--core/java/com/android/internal/os/IBinaryTransparencyService.aidl30
-rw-r--r--core/java/com/android/internal/os/OWNERS1
-rw-r--r--services/core/java/com/android/server/BinaryTransparencyService.java525
-rw-r--r--services/core/java/com/android/server/OWNERS1
-rw-r--r--services/java/com/android/server/SystemServer.java4
10 files changed, 676 insertions, 0 deletions
diff --git a/core/java/Android.bp b/core/java/Android.bp
index c9cbef2c9e9a..376796295043 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -29,6 +29,11 @@ filegroup {
}
filegroup {
+ name: "IBinaryTransparencyService.aidl",
+ srcs: ["com/android/internal/os/IBinaryTransparencyService.aidl"],
+}
+
+filegroup {
name: "ITracingServiceProxy.aidl",
srcs: ["android/tracing/ITracingServiceProxy.aidl"],
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 85ddff9a9311..92d395e623d8 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -212,6 +212,7 @@ import android.telecom.TelecomManager;
import android.telephony.MmsManager;
import android.telephony.TelephonyFrameworkInitializer;
import android.telephony.TelephonyRegistryManager;
+import android.transparency.BinaryTransparencyManager;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
@@ -242,6 +243,7 @@ import com.android.internal.app.ISoundTriggerService;
import com.android.internal.appwidget.IAppWidgetService;
import com.android.internal.graphics.fonts.IFontManager;
import com.android.internal.net.INetworkWatchlistManager;
+import com.android.internal.os.IBinaryTransparencyService;
import com.android.internal.os.IDropBoxManagerService;
import com.android.internal.policy.PhoneLayoutInflater;
import com.android.internal.util.Preconditions;
@@ -500,6 +502,17 @@ public final class SystemServiceRegistry {
return new DropBoxManager(ctx, service);
}});
+ registerService(Context.BINARY_TRANSPARENCY_SERVICE, BinaryTransparencyManager.class,
+ new CachedServiceFetcher<BinaryTransparencyManager>() {
+ @Override
+ public BinaryTransparencyManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ IBinder b = ServiceManager.getServiceOrThrow(
+ Context.BINARY_TRANSPARENCY_SERVICE);
+ IBinaryTransparencyService service = IBinaryTransparencyService.Stub.asInterface(b);
+ return new BinaryTransparencyManager(ctx, service);
+ }});
+
registerService(Context.INPUT_SERVICE, InputManager.class,
new StaticServiceFetcher<InputManager>() {
@Override
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index bccfacf70842..b4195df6f0b8 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -5076,6 +5076,16 @@ public abstract class Context {
public static final String DROPBOX_SERVICE = "dropbox";
/**
+ * System service name for BinaryTransparencyService. This is used to retrieve measurements
+ * pertaining to various pre-installed and system binaries on device for the purposes of
+ * providing transparency to the user.
+ *
+ * @hide
+ */
+ @SuppressLint("ServiceName")
+ public static final String BINARY_TRANSPARENCY_SERVICE = "transparency";
+
+ /**
* System service name for the DeviceIdleManager.
* @see #getSystemService(String)
* @hide
diff --git a/core/java/android/transparency/BinaryTransparencyManager.java b/core/java/android/transparency/BinaryTransparencyManager.java
new file mode 100644
index 000000000000..18783f507085
--- /dev/null
+++ b/core/java/android/transparency/BinaryTransparencyManager.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2022 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.transparency;
+
+import android.annotation.NonNull;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.internal.os.IBinaryTransparencyService;
+
+import java.util.Map;
+
+/**
+ * BinaryTransparencyManager defines a number of system interfaces that other system apps or
+ * services can make use of, when trying to get more information about the various binaries
+ * that are installed on this device.
+ * @hide
+ */
+@SystemService(Context.BINARY_TRANSPARENCY_SERVICE)
+public class BinaryTransparencyManager {
+ private static final String TAG = "TransparencyManager";
+
+ private final Context mContext;
+ private final IBinaryTransparencyService mService;
+
+ /**
+ * Constructor
+ * @param context The calling context.
+ * @param service A valid instance of IBinaryTransparencyService.
+ * @hide
+ */
+ public BinaryTransparencyManager(Context context, IBinaryTransparencyService service) {
+ mContext = context;
+ mService = service;
+ }
+
+
+ /**
+ * Obtains a string containing information that describes the signed images that are installed
+ * on this device. Currently, this piece of information is identified as the VBMeta digest.
+ * @return A String containing the VBMeta Digest of the signed partitions loaded on this device.
+ */
+ @NonNull
+ public String getSignedImageInfo() {
+ try {
+ return mService.getSignedImageInfo();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns a map of all installed APEXs consisting of package name to SHA256 hash of the
+ * package.
+ * @return A Map with the following entries: {apex package name : sha256 digest of package}
+ */
+ @NonNull
+ public Map getApexInfo() {
+ try {
+ Slog.d(TAG, "Calling backend's getApexInfo()");
+ return mService.getApexInfo();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+}
diff --git a/core/java/android/transparency/OWNERS b/core/java/android/transparency/OWNERS
new file mode 100644
index 000000000000..75bf84c727df
--- /dev/null
+++ b/core/java/android/transparency/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 36824
+billylau@google.com
+vishwath@google.com
+mpgroover@google.com
diff --git a/core/java/com/android/internal/os/IBinaryTransparencyService.aidl b/core/java/com/android/internal/os/IBinaryTransparencyService.aidl
new file mode 100644
index 000000000000..9be686a772c1
--- /dev/null
+++ b/core/java/com/android/internal/os/IBinaryTransparencyService.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 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.internal.os;
+
+/**
+ * "Backend" interface used by {@link android.os.BinaryTransparencyManager} to talk to the
+ * BinaryTransparencyService that actually implements the measurement and information aggregation
+ * functionality.
+ *
+ * @see BinaryTransparencyManager
+ */
+interface IBinaryTransparencyService {
+ String getSignedImageInfo();
+
+ Map getApexInfo();
+} \ No newline at end of file
diff --git a/core/java/com/android/internal/os/OWNERS b/core/java/com/android/internal/os/OWNERS
index 7766b77ab3c6..276ad4c4f30e 100644
--- a/core/java/com/android/internal/os/OWNERS
+++ b/core/java/com/android/internal/os/OWNERS
@@ -2,6 +2,7 @@ per-file *Power* = file:/services/core/java/com/android/server/power/OWNERS
per-file *Zygote* = file:/ZYGOTE_OWNERS
per-file *Cpu* = file:CPU_OWNERS
per-file *Binder* = file:BINDER_OWNERS
+per-file *BinaryTransparency* = file:/core/java/android/transparency/OWNERS
# BatteryStats
per-file BatterySipper.java = file:/BATTERY_STATS_OWNERS
diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java
new file mode 100644
index 000000000000..db510cb6422a
--- /dev/null
+++ b/services/core/java/com/android/server/BinaryTransparencyService.java
@@ -0,0 +1,525 @@
+/*
+ * Copyright (C) 2022 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;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ModuleInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.os.ShellCommand;
+import android.os.SystemProperties;
+import android.util.PackageUtils;
+import android.util.Slog;
+
+import com.android.internal.os.IBinaryTransparencyService;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * @hide
+ */
+public class BinaryTransparencyService extends SystemService {
+ private static final String TAG = "TransparencyService";
+
+ private static final String VBMETA_DIGEST_UNINITIALIZED = "vbmeta-digest-uninitialized";
+ private static final String VBMETA_DIGEST_UNAVAILABLE = "vbmeta-digest-unavailable";
+ private static final String SYSPROP_NAME_VBETA_DIGEST = "ro.boot.vbmeta.digest";
+
+ private static final String BINARY_HASH_ERROR = "SHA256HashError";
+
+ private final Context mContext;
+ private String mVbmetaDigest;
+ private HashMap<String, String> mBinaryHashes;
+ private HashMap<String, Long> mBinaryLastUpdateTimes;
+
+ final class BinaryTransparencyServiceImpl extends IBinaryTransparencyService.Stub {
+
+ @Override
+ public String getSignedImageInfo() {
+ return mVbmetaDigest;
+ }
+
+ @Override
+ public Map getApexInfo() {
+ HashMap results = new HashMap();
+ if (!updateBinaryMeasurements()) {
+ Slog.e(TAG, "Error refreshing APEX measurements.");
+ return results;
+ }
+ PackageManager pm = mContext.getPackageManager();
+ if (pm == null) {
+ Slog.e(TAG, "Error obtaining an instance of PackageManager.");
+ return results;
+ }
+
+ for (PackageInfo packageInfo : getInstalledApexs()) {
+ results.put(packageInfo, mBinaryHashes.get(packageInfo.packageName));
+ }
+
+ return results;
+ }
+
+ @Override
+ public void onShellCommand(@Nullable FileDescriptor in,
+ @Nullable FileDescriptor out,
+ @Nullable FileDescriptor err,
+ @NonNull String[] args,
+ @Nullable ShellCallback callback,
+ @NonNull ResultReceiver resultReceiver) throws RemoteException {
+ (new ShellCommand() {
+
+ private int printSignedImageInfo() {
+ final PrintWriter pw = getOutPrintWriter();
+ boolean listAllPartitions = false;
+ String opt;
+
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "-a":
+ listAllPartitions = true;
+ break;
+ default:
+ pw.println("ERROR: Unknown option: " + opt);
+ return 1;
+ }
+ }
+
+ final String signedImageInfo = getSignedImageInfo();
+ pw.println("Image Info:");
+ pw.println(Build.FINGERPRINT);
+ pw.println(signedImageInfo);
+ pw.println("");
+
+ if (listAllPartitions) {
+ PackageManager pm = mContext.getPackageManager();
+ if (pm == null) {
+ pw.println("ERROR: Failed to obtain an instance of package manager.");
+ return -1;
+ }
+
+ pw.println("Other partitions:");
+ List<Build.Partition> buildPartitions = Build.getFingerprintedPartitions();
+ for (Build.Partition buildPartition : buildPartitions) {
+ pw.println("Name: " + buildPartition.getName());
+ pw.println("Fingerprint: " + buildPartition.getFingerprint());
+ pw.println("Build time (ms): " + buildPartition.getBuildTimeMillis());
+ }
+ }
+ return 0;
+ }
+
+ private void printModuleDetails(ModuleInfo moduleInfo, final PrintWriter pw) {
+ pw.println("--- Module Details ---");
+ pw.println("Module name: " + moduleInfo.getName());
+ pw.println("Module visibility: "
+ + (moduleInfo.isHidden() ? "hidden" : "visible"));
+ }
+
+ private int printAllApexs() {
+ final PrintWriter pw = getOutPrintWriter();
+ boolean verbose = false;
+ String opt;
+
+ // refresh cache to make sure info is most up-to-date
+ if (!updateBinaryMeasurements()) {
+ pw.println("ERROR: Failed to refresh info for APEXs.");
+ return -1;
+ }
+ if (mBinaryHashes == null || (mBinaryHashes.size() == 0)) {
+ pw.println("ERROR: Unable to obtain apex_info at this time.");
+ return -1;
+ }
+
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "-v":
+ verbose = true;
+ break;
+ default:
+ pw.println("ERROR: Unknown option: " + opt);
+ return 1;
+ }
+ }
+
+ PackageManager pm = mContext.getPackageManager();
+ if (pm == null) {
+ pw.println("ERROR: Failed to obtain an instance of package manager.");
+ return -1;
+ }
+
+ pw.println("APEX Info:");
+ for (PackageInfo packageInfo : getInstalledApexs()) {
+ String packageName = packageInfo.packageName;
+ pw.println(packageName + ";"
+ + packageInfo.getLongVersionCode() + ":"
+ + mBinaryHashes.get(packageName).toLowerCase());
+
+ if (verbose) {
+ pw.println("Install location: "
+ + packageInfo.applicationInfo.sourceDir);
+ pw.println("Last Update Time (ms): " + packageInfo.lastUpdateTime);
+
+ ModuleInfo moduleInfo;
+ try {
+ moduleInfo = pm.getModuleInfo(packageInfo.packageName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ pw.println("Is A Module: False");
+ pw.println("");
+ continue;
+ }
+ pw.println("Is A Module: True");
+ printModuleDetails(moduleInfo, pw);
+ pw.println("");
+ }
+ }
+ return 0;
+ }
+
+ private int printAllModules() {
+ final PrintWriter pw = getOutPrintWriter();
+ boolean verbose = false;
+ String opt;
+
+ // refresh cache to make sure info is most up-to-date
+ if (!updateBinaryMeasurements()) {
+ pw.println("ERROR: Failed to refresh info for Modules.");
+ return -1;
+ }
+ if (mBinaryHashes == null || (mBinaryHashes.size() == 0)) {
+ pw.println("ERROR: Unable to obtain module_info at this time.");
+ return -1;
+ }
+
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "-v":
+ verbose = true;
+ break;
+ default:
+ pw.println("ERROR: Unknown option: " + opt);
+ return 1;
+ }
+ }
+
+ PackageManager pm = mContext.getPackageManager();
+ if (pm == null) {
+ pw.println("ERROR: Failed to obtain an instance of package manager.");
+ return -1;
+ }
+
+ pw.println("Module Info:");
+ for (ModuleInfo module : pm.getInstalledModules(PackageManager.MATCH_ALL)) {
+ String packageName = module.getPackageName();
+ try {
+ PackageInfo packageInfo = pm.getPackageInfo(packageName,
+ PackageManager.MATCH_APEX);
+ pw.println(packageInfo.packageName + ";"
+ + packageInfo.getLongVersionCode() + ":"
+ + mBinaryHashes.get(packageName).toLowerCase());
+
+ if (verbose) {
+ pw.println("Install location: "
+ + packageInfo.applicationInfo.sourceDir);
+ printModuleDetails(module, pw);
+ pw.println("");
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ pw.println(packageName);
+ pw.println(packageName
+ + ";ERROR:Unable to find PackageInfo for this module.");
+ if (verbose) {
+ printModuleDetails(module, pw);
+ pw.println("");
+ }
+ continue;
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+
+ final PrintWriter pw = getOutPrintWriter();
+ switch (cmd) {
+ case "get": {
+ final String infoType = getNextArg();
+ if (infoType == null) {
+ printHelpMenu();
+ return -1;
+ }
+
+ switch (infoType) {
+ case "image_info":
+ return printSignedImageInfo();
+ case "apex_info":
+ return printAllApexs();
+ case "module_info":
+ return printAllModules();
+ default:
+ pw.println(String.format("ERROR: Unknown info type '%s'",
+ infoType));
+ return 1;
+ }
+ }
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ }
+
+ private void printHelpMenu() {
+ final PrintWriter pw = getOutPrintWriter();
+ pw.println("Transparency manager (transparency) commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println("");
+ pw.println(" get image_info [-a]");
+ pw.println(" Print information about loaded image (firmware). Options:");
+ pw.println(" -a: lists all other identifiable partitions.");
+ pw.println("");
+ pw.println(" get apex_info [-v]");
+ pw.println(" Print information about installed APEXs on device.");
+ pw.println(" -v: lists more verbose information about each APEX");
+ pw.println("");
+ pw.println(" get module_info [-v]");
+ pw.println(" Print information about installed modules on device.");
+ pw.println(" -v: lists more verbose information about each module");
+ pw.println("");
+ }
+
+ @Override
+ public void onHelp() {
+ printHelpMenu();
+ }
+ }).exec(this, in, out, err, args, callback, resultReceiver);
+ }
+ }
+ private final BinaryTransparencyServiceImpl mServiceImpl;
+
+ public BinaryTransparencyService(Context context) {
+ super(context);
+ mContext = context;
+ mServiceImpl = new BinaryTransparencyServiceImpl();
+ mVbmetaDigest = VBMETA_DIGEST_UNINITIALIZED;
+ mBinaryHashes = new HashMap<>();
+ mBinaryLastUpdateTimes = new HashMap<>();
+ }
+
+ /**
+ * Called when the system service should publish a binder service using
+ * {@link #publishBinderService(String, IBinder).}
+ */
+ @Override
+ public void onStart() {
+ try {
+ publishBinderService(Context.BINARY_TRANSPARENCY_SERVICE, mServiceImpl);
+ Slog.i(TAG, "Started BinaryTransparencyService");
+ } catch (Throwable t) {
+ Slog.e(TAG, "Failed to start BinaryTransparencyService.", t);
+ }
+ }
+
+ /**
+ * Called on each phase of the boot process. Phases before the service's start phase
+ * (as defined in the @Service annotation) are never received.
+ *
+ * @param phase The current boot phase.
+ */
+ @Override
+ public void onBootPhase(int phase) {
+
+ // we are only interested in doing things at PHASE_BOOT_COMPLETED
+ if (phase == PHASE_BOOT_COMPLETED) {
+ // due to potentially long computation that holds up boot time, apex sha computations
+ // are deferred to first call
+ Slog.i(TAG, "Boot completed. Getting VBMeta Digest.");
+ getVBMetaDigestInformation();
+ }
+ }
+
+ private void getVBMetaDigestInformation() {
+ mVbmetaDigest = SystemProperties.get(SYSPROP_NAME_VBETA_DIGEST, VBMETA_DIGEST_UNAVAILABLE);
+ Slog.d(TAG, String.format("VBMeta Digest: %s", mVbmetaDigest));
+ }
+
+ @NonNull
+ private List<PackageInfo> getInstalledApexs() {
+ List<PackageInfo> results = new ArrayList<PackageInfo>();
+ PackageManager pm = mContext.getPackageManager();
+ if (pm == null) {
+ Slog.e(TAG, "Error obtaining an instance of PackageManager.");
+ return results;
+ }
+ List<PackageInfo> allPackages = pm.getInstalledPackages(
+ PackageManager.PackageInfoFlags.of(PackageManager.MATCH_APEX));
+ if (allPackages == null) {
+ Slog.e(TAG, "Error obtaining installed packages (including APEX)");
+ return results;
+ }
+
+ results = allPackages.stream().filter(p -> p.isApex).collect(Collectors.toList());
+ return results;
+ }
+
+
+ /**
+ * Updates the internal data structure with the most current APEX measurements.
+ * @return true if update is successful; false otherwise.
+ */
+ private boolean updateBinaryMeasurements() {
+ if (mBinaryHashes.size() == 0) {
+ Slog.d(TAG, "No apex in cache yet.");
+ doFreshBinaryMeasurements();
+ return true;
+ }
+
+ PackageManager pm = mContext.getPackageManager();
+ if (pm == null) {
+ Slog.e(TAG, "Failed to obtain a valid PackageManager instance.");
+ return false;
+ }
+
+ // We're assuming updates to existing modules and APEXs can happen, but not brand new
+ // ones appearing out of the blue. Thus, we're going to only go through our cache to check
+ // for changes, rather than freshly invoking `getInstalledPackages()` and
+ // `getInstalledModules()`
+ for (Map.Entry<String, Long> entry : mBinaryLastUpdateTimes.entrySet()) {
+ String packageName = entry.getKey();
+ try {
+ PackageInfo packageInfo = pm.getPackageInfo(packageName,
+ PackageManager.PackageInfoFlags.of(PackageManager.MATCH_APEX));
+ long cachedUpdateTime = entry.getValue();
+
+ if (packageInfo.lastUpdateTime > cachedUpdateTime) {
+ Slog.d(TAG, packageName + " has been updated!");
+ entry.setValue(packageInfo.lastUpdateTime);
+
+ // compute the digest for the updated package
+ String sha256digest = computeSha256DigestOfFile(
+ packageInfo.applicationInfo.sourceDir);
+ if (sha256digest == null) {
+ Slog.e(TAG, "Failed to compute SHA256sum for file at "
+ + packageInfo.applicationInfo.sourceDir);
+ mBinaryHashes.put(packageName, BINARY_HASH_ERROR);
+ } else {
+ mBinaryHashes.put(packageName, sha256digest);
+ }
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, "Could not find package with name " + packageName);
+ continue;
+ }
+ }
+
+ return true;
+ }
+
+ private void doFreshBinaryMeasurements() {
+ PackageManager pm = mContext.getPackageManager();
+ Slog.d(TAG, "Obtained package manager");
+
+ // In general, we care about all APEXs, *and* all Modules, which may include some APKs.
+
+ // First, we deal with all installed APEXs.
+ for (PackageInfo packageInfo : getInstalledApexs()) {
+ ApplicationInfo appInfo = packageInfo.applicationInfo;
+
+ // compute SHA256 for these APEXs
+ String sha256digest = computeSha256DigestOfFile(appInfo.sourceDir);
+ if (sha256digest == null) {
+ Slog.e(TAG, String.format("Failed to compute SHA256 digest for %s",
+ packageInfo.packageName));
+ mBinaryHashes.put(packageInfo.packageName, BINARY_HASH_ERROR);
+ } else {
+ mBinaryHashes.put(packageInfo.packageName, sha256digest);
+ }
+ Slog.d(TAG, String.format("Last update time for %s: %d", packageInfo.packageName,
+ packageInfo.lastUpdateTime));
+ mBinaryLastUpdateTimes.put(packageInfo.packageName, packageInfo.lastUpdateTime);
+ }
+
+ // Next, get all installed modules from PackageManager - skip over those APEXs we've
+ // processed above
+ for (ModuleInfo module : pm.getInstalledModules(PackageManager.MATCH_ALL)) {
+ String packageName = module.getPackageName();
+ if (packageName == null) {
+ Slog.e(TAG, "ERROR: Encountered null package name for module "
+ + module.getApexModuleName());
+ continue;
+ }
+ if (mBinaryHashes.containsKey(module.getPackageName())) {
+ continue;
+ }
+
+ // get PackageInfo for this module
+ try {
+ PackageInfo packageInfo = pm.getPackageInfo(packageName,
+ PackageManager.PackageInfoFlags.of(PackageManager.MATCH_APEX));
+ ApplicationInfo appInfo = packageInfo.applicationInfo;
+
+ // compute SHA256 digest for these modules
+ String sha256digest = computeSha256DigestOfFile(appInfo.sourceDir);
+ if (sha256digest == null) {
+ Slog.e(TAG, String.format("Failed to compute SHA256 digest for %s",
+ packageName));
+ mBinaryHashes.put(packageName, BINARY_HASH_ERROR);
+ } else {
+ mBinaryHashes.put(packageName, sha256digest);
+ }
+ Slog.d(TAG, String.format("Last update time for %s: %d", packageName,
+ packageInfo.lastUpdateTime));
+ mBinaryLastUpdateTimes.put(packageName, packageInfo.lastUpdateTime);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, "ERROR: Could not obtain PackageInfo for package name: "
+ + packageName);
+ continue;
+ }
+ }
+ }
+
+ @Nullable
+ private String computeSha256DigestOfFile(@NonNull String pathToFile) {
+ File apexFile = new File(pathToFile);
+
+ try {
+ byte[] apexFileBytes = Files.readAllBytes(apexFile.toPath());
+ return PackageUtils.computeSha256Digest(apexFileBytes);
+ } catch (IOException e) {
+ Slog.e(TAG, String.format("I/O error occurs when reading from %s", pathToFile));
+ return null;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index 71b463a4db8c..efdd7abe85de 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -22,6 +22,7 @@ per-file BatteryService.java = file:platform/hardware/interfaces:/health/aidl/OW
per-file *Alarm* = file:/apex/jobscheduler/OWNERS
per-file *AppOp* = file:/core/java/android/permission/OWNERS
per-file *Battery* = file:/BATTERY_STATS_OWNERS
+per-file *BinaryTransparency* = file:/core/java/android/transparency/OWNERS
per-file *Binder* = file:/core/java/com/android/internal/os/BINDER_OWNERS
per-file *Bluetooth* = file:/core/java/android/bluetooth/OWNERS
per-file *Gnss* = file:/services/core/java/com/android/server/location/OWNERS
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 29797a549e49..3be890c07c8e 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1456,6 +1456,10 @@ public final class SystemServer implements Dumpable {
mSystemServiceManager.startService(KeyChainSystemService.class);
t.traceEnd();
+ t.traceBegin("StartBinaryTransparencyService");
+ mSystemServiceManager.startService(BinaryTransparencyService.class);
+ t.traceEnd();
+
t.traceBegin("StartSchedulingPolicyService");
ServiceManager.addService("scheduling_policy", new SchedulingPolicyService());
t.traceEnd();