diff options
| author | 2022-01-20 15:42:17 -0800 | |
|---|---|---|
| committer | 2022-01-24 15:58:05 -0800 | |
| commit | d1fb08be0a3e4415ae5ac9935ca51a8c02ad90c1 (patch) | |
| tree | ceacdc8711284c67f31c667006bbcf9eb288eec4 | |
| parent | 5a6ec226765ecddcaee8274a9ae24fa8ee35717a (diff) | |
Add BinaryTransparencyService as new SystemService.
Bug: 197684182
Created a new system service that aggregates information about:
1) signed partitions on device
2) APEXs installed on device
3) Modules installed on device
Introduces new adb shell commands as below:
adb shell cmd transparency get image_info [-a]
adb shell cmd transparency get apex_info [-v]
adb shell cmd transparency get module_info [-v]
Test: Built and tested locally on a bramble - the device boots.
Issuing adb shell commands as above provides correct outputs.
Change-Id: I2cae1bd794456688779c70c65b3f5ec8bcd7c6b3
| -rw-r--r-- | core/java/Android.bp | 5 | ||||
| -rw-r--r-- | core/java/android/app/SystemServiceRegistry.java | 13 | ||||
| -rw-r--r-- | core/java/android/content/Context.java | 10 | ||||
| -rw-r--r-- | core/java/android/transparency/BinaryTransparencyManager.java | 83 | ||||
| -rw-r--r-- | core/java/android/transparency/OWNERS | 4 | ||||
| -rw-r--r-- | core/java/com/android/internal/os/IBinaryTransparencyService.aidl | 30 | ||||
| -rw-r--r-- | core/java/com/android/internal/os/OWNERS | 1 | ||||
| -rw-r--r-- | services/core/java/com/android/server/BinaryTransparencyService.java | 525 | ||||
| -rw-r--r-- | services/core/java/com/android/server/OWNERS | 1 | ||||
| -rw-r--r-- | services/java/com/android/server/SystemServer.java | 4 |
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(); |