• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.SuppressLint;
22 import android.apex.ApexInfo;
23 import android.apex.IApexService;
24 import android.app.compat.CompatChanges;
25 import android.app.job.JobInfo;
26 import android.app.job.JobParameters;
27 import android.app.job.JobScheduler;
28 import android.app.job.JobService;
29 import android.compat.annotation.ChangeId;
30 import android.content.BroadcastReceiver;
31 import android.content.ComponentName;
32 import android.content.Context;
33 import android.content.Intent;
34 import android.content.IntentFilter;
35 import android.content.pm.ApexStagedEvent;
36 import android.content.pm.ApplicationInfo;
37 import android.content.pm.IBackgroundInstallControlService;
38 import android.content.pm.IPackageManagerNative;
39 import android.content.pm.IStagedApexObserver;
40 import android.content.pm.InstallSourceInfo;
41 import android.content.pm.ModuleInfo;
42 import android.content.pm.PackageInfo;
43 import android.content.pm.PackageManager;
44 import android.content.pm.PackageManagerInternal;
45 import android.content.pm.ParceledListSlice;
46 import android.content.pm.SharedLibraryInfo;
47 import android.content.pm.Signature;
48 import android.content.pm.SigningDetails;
49 import android.content.pm.SigningInfo;
50 import android.content.pm.parsing.result.ParseInput;
51 import android.content.pm.parsing.result.ParseResult;
52 import android.content.pm.parsing.result.ParseTypeImpl;
53 import android.hardware.biometrics.SensorProperties;
54 import android.hardware.biometrics.SensorProperties.ComponentInfo;
55 import android.hardware.face.FaceManager;
56 import android.hardware.face.FaceSensorProperties;
57 import android.hardware.face.FaceSensorPropertiesInternal;
58 import android.hardware.face.IFaceAuthenticatorsRegisteredCallback;
59 import android.hardware.fingerprint.FingerprintManager;
60 import android.hardware.fingerprint.FingerprintSensorProperties;
61 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
62 import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
63 import android.net.Uri;
64 import android.os.Binder;
65 import android.os.Build;
66 import android.os.Bundle;
67 import android.os.IBinder;
68 import android.os.IRemoteCallback;
69 import android.os.RemoteException;
70 import android.os.ResultReceiver;
71 import android.os.ServiceManager;
72 import android.os.ShellCallback;
73 import android.os.ShellCommand;
74 import android.os.SystemProperties;
75 import android.os.UserHandle;
76 import android.provider.DeviceConfig;
77 import android.text.TextUtils;
78 import android.util.PackageUtils;
79 import android.util.Slog;
80 import android.util.apk.ApkSignatureVerifier;
81 import android.util.apk.ApkSigningBlockUtils;
82 
83 import com.android.internal.annotations.VisibleForTesting;
84 import com.android.internal.os.IBinaryTransparencyService;
85 import com.android.internal.util.FrameworkStatsLog;
86 import com.android.modules.expresslog.Histogram;
87 import com.android.server.pm.ApexManager;
88 import com.android.server.pm.BackgroundInstallControlCallbackHelper;
89 import com.android.server.pm.BackgroundInstallControlService;
90 import com.android.server.pm.pkg.AndroidPackage;
91 import com.android.server.pm.pkg.AndroidPackageSplit;
92 import com.android.server.pm.pkg.PackageState;
93 
94 import libcore.util.HexEncoding;
95 
96 import java.io.FileDescriptor;
97 import java.io.PrintWriter;
98 import java.security.PublicKey;
99 import java.security.cert.CertificateException;
100 import java.util.ArrayList;
101 import java.util.List;
102 import java.util.Map;
103 import java.util.concurrent.Executors;
104 import java.util.stream.Collectors;
105 
106 /**
107  * @hide
108  */
109 public class BinaryTransparencyService extends SystemService {
110     private static final String TAG = "TransparencyService";
111 
112     @VisibleForTesting
113     static final String VBMETA_DIGEST_UNINITIALIZED = "vbmeta-digest-uninitialized";
114     @VisibleForTesting
115     static final String VBMETA_DIGEST_UNAVAILABLE = "vbmeta-digest-unavailable";
116     @VisibleForTesting
117     static final String SYSPROP_NAME_VBETA_DIGEST = "ro.boot.vbmeta.digest";
118 
119     @VisibleForTesting
120     static final String BINARY_HASH_ERROR = "SHA256HashError";
121 
122     static final long RECORD_MEASUREMENTS_COOLDOWN_MS = 24 * 60 * 60 * 1000;
123 
124     static final String APEX_PRELOAD_LOCATION_ERROR = "could-not-be-determined";
125 
126     // Copy from the atom. Consistent for both ApexInfoGathered and MobileBundledAppInfoGathered.
127     static final int DIGEST_ALGORITHM_UNKNOWN = 0;
128     static final int DIGEST_ALGORITHM_CHUNKED_SHA256 = 1;
129     static final int DIGEST_ALGORITHM_CHUNKED_SHA512 = 2;
130     static final int DIGEST_ALGORITHM_VERITY_CHUNKED_SHA256 = 3;
131     static final int DIGEST_ALGORITHM_SHA256 = 4;
132 
133     // used for indicating any type of error during MBA measurement
134     static final int MBA_STATUS_ERROR = 0;
135     // used for indicating factory condition preloads
136     static final int MBA_STATUS_PRELOADED = 1;
137     // used for indicating preloaded apps that are updated
138     static final int MBA_STATUS_UPDATED_PRELOAD = 2;
139     // used for indicating newly installed MBAs
140     static final int MBA_STATUS_NEW_INSTALL = 3;
141     // used for indicating newly installed MBAs that are updated (but unused currently)
142     static final int MBA_STATUS_UPDATED_NEW_INSTALL = 4;
143     // used for indicating preloaded MBAs that are downgraded
144     static final int MBA_STATUS_DOWNGRADED_PRELOADED = 5;
145     // used for indicating MBAs that are uninstalled
146     static final int MBA_STATUS_UNINSTALLED = 6;
147 
148     @VisibleForTesting
149     static final String KEY_ENABLE_BIOMETRIC_PROPERTY_VERIFICATION =
150             "enable_biometric_property_verification";
151 
152     private static final boolean DEBUG = false;     // toggle this for local debug
153 
154     private static final Histogram digestAllPackagesLatency = new Histogram(
155             "binary_transparency.value_digest_all_packages_latency_uniform",
156             new Histogram.UniformOptions(50, 0, 500));
157 
158     private final Context mContext;
159     private String mVbmetaDigest;
160     // the system time (in ms) the last measurement was taken
161     private long mMeasurementsLastRecordedMs;
162     private PackageManagerInternal mPackageManagerInternal;
163     private BiometricLogger mBiometricLogger;
164 
165     /**
166      * Guards whether or not measurements of MBA to be performed. When this change is enabled,
167      * measurements of MBAs are performed. But when it is disabled, only measurements of APEX
168      * and modules are done.
169      */
170     @ChangeId
171     public static final long LOG_MBA_INFO = 245692487L;
172 
173     final class BinaryTransparencyServiceImpl extends IBinaryTransparencyService.Stub {
174 
175         @Override
getSignedImageInfo()176         public String getSignedImageInfo() {
177             return mVbmetaDigest;
178         }
179 
180         /**
181          * A helper function to compute the SHA256 digest of APK package signer.
182          * @param signingInfo The signingInfo of a package, usually {@link PackageInfo#signingInfo}.
183          * @return an array of {@code String} representing hex encoded string of the
184          *         SHA256 digest of APK signer(s). The number of signers will be reflected by the
185          *         size of the array.
186          *         However, {@code null} is returned if there is any error.
187          */
computePackageSignerSha256Digests(@ullable SigningInfo signingInfo)188         private String[] computePackageSignerSha256Digests(@Nullable SigningInfo signingInfo) {
189             if (signingInfo == null) {
190                 Slog.e(TAG, "signingInfo is null");
191                 return null;
192             }
193 
194             Signature[] packageSigners = signingInfo.getApkContentsSigners();
195             List<String> resultList = new ArrayList<>();
196             for (Signature packageSigner : packageSigners) {
197                 byte[] digest = PackageUtils.computeSha256DigestBytes(packageSigner.toByteArray());
198                 String digestHexString = HexEncoding.encodeToString(digest, false);
199                 resultList.add(digestHexString);
200             }
201             return resultList.toArray(new String[1]);
202         }
203 
204         /*
205          * Perform basic measurement (i.e. content digest) on a given app, including the split APKs.
206          *
207          * @param packageState The package to be measured.
208          * @param mbaStatus Assign this value of MBA status to the returned elements.
209          * @return a @{@code List<IBinaryTransparencyService.AppInfo>}
210          */
211         @VisibleForTesting
212         @NonNull
collectAppInfo( PackageState packageState, int mbaStatus)213         List<IBinaryTransparencyService.AppInfo> collectAppInfo(
214                 PackageState packageState, int mbaStatus) {
215             // compute content digest
216             if (DEBUG) {
217                 Slog.d(TAG, "Computing content digest for " + packageState.getPackageName() + " at "
218                         + packageState.getPath());
219             }
220 
221             var results = new ArrayList<IBinaryTransparencyService.AppInfo>();
222 
223             // Same attributes across base and splits.
224             String packageName = packageState.getPackageName();
225             long versionCode = packageState.getVersionCode();
226             String[] signerDigests =
227                     computePackageSignerSha256Digests(packageState.getSigningInfo());
228 
229             AndroidPackage pkg = packageState.getAndroidPackage();
230             if(pkg != null) {
231                 for (AndroidPackageSplit split : pkg.getSplits()) {
232                     var appInfo = new IBinaryTransparencyService.AppInfo();
233                     appInfo.packageName = packageName;
234                     appInfo.longVersion = versionCode;
235                     appInfo.splitName = split.getName();  // base's split name is null
236                     // Signer digests are consistent between splits, guaranteed by Package Manager.
237                     appInfo.signerDigests = signerDigests;
238                     appInfo.mbaStatus = mbaStatus;
239 
240                     // Only digest and split name are different between splits.
241                     Digest digest = measureApk(split.getPath());
242                     appInfo.digest = digest.value();
243                     appInfo.digestAlgorithm = digest.algorithm();
244 
245                     results.add(appInfo);
246                 }
247             } else {
248                 Slog.w(TAG, packageName + " APK file is not physically present,"
249                     + " skipping split and digest measurement");
250                 var appInfo = new IBinaryTransparencyService.AppInfo();
251                 appInfo.packageName = packageName;
252                 appInfo.longVersion = versionCode;
253                 appInfo.signerDigests = signerDigests;
254                 appInfo.mbaStatus = mbaStatus;
255                 results.add(appInfo);
256             }
257 
258             // InstallSourceInfo is only available per package name, so store it only on the base
259             // APK. It's not current currently available in PackageState (there's a TODO), to we
260             // need to extract manually with another call.
261             //
262             // Base APK is already the 0-th split from getSplits() and can't be null.
263             AppInfo base = results.get(0);
264             InstallSourceInfo installSourceInfo = getInstallSourceInfo(
265                     packageState.getPackageName());
266             if (installSourceInfo != null) {
267                 base.initiator = installSourceInfo.getInitiatingPackageName();
268                 SigningInfo initiatorSignerInfo =
269                         installSourceInfo.getInitiatingPackageSigningInfo();
270                 if (initiatorSignerInfo != null) {
271                     base.initiatorSignerDigests =
272                         computePackageSignerSha256Digests(initiatorSignerInfo);
273                 }
274                 base.installer = installSourceInfo.getInstallingPackageName();
275                 base.originator = installSourceInfo.getOriginatingPackageName();
276             }
277 
278             return results;
279         }
280 
281         /**
282          * Perform basic measurement (i.e. content digest) on a given APK.
283          *
284          * @param apkPath The APK (or APEX, since it's also an APK) file to be measured.
285          * @return a {@link #Digest} with preferred digest algorithm type and the value.
286          */
measureApk(@onNull String apkPath)287         private @Nullable Digest measureApk(@NonNull String apkPath) {
288             // compute content digest
289             Map<Integer, byte[]> contentDigests = computeApkContentDigest(apkPath);
290             if (contentDigests == null) {
291                 Slog.d(TAG, "Failed to compute content digest for " + apkPath);
292             } else {
293                 // in this iteration, we'll be supporting only 2 types of digests:
294                 // CHUNKED_SHA256 and CHUNKED_SHA512.
295                 // And only one of them will be available per package.
296                 if (contentDigests.containsKey(
297                             ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256)) {
298                     return new Digest(
299                             DIGEST_ALGORITHM_CHUNKED_SHA256,
300                             contentDigests.get(ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256));
301                 } else if (contentDigests.containsKey(
302                         ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512)) {
303                     return new Digest(
304                             DIGEST_ALGORITHM_CHUNKED_SHA512,
305                             contentDigests.get(ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512));
306                 }
307             }
308             // When something went wrong, fall back to simple sha256.
309             byte[] digest = PackageUtils.computeSha256DigestForLargeFileAsBytes(apkPath,
310                     PackageUtils.createLargeFileBuffer());
311             return new Digest(DIGEST_ALGORITHM_SHA256, digest);
312         }
313 
314 
315         /**
316          * Measures and records digests for *all* covered binaries/packages.
317          *
318          * This method will be called in a Job scheduled to take measurements periodically. If the
319          * last measurement was performaned recently (less than RECORD_MEASUREMENT_COOLDOWN_MS
320          * ago), the measurement and recording will be skipped.
321          *
322          * Packages that are covered so far are:
323          * - all APEXs (introduced in Android T)
324          * - all mainline modules (introduced in Android T)
325          * - all preloaded apps and their update(s) (new in Android U)
326          * - dynamically installed mobile bundled apps (MBAs) (new in Android U)
327          */
recordMeasurementsForAllPackages()328         public void recordMeasurementsForAllPackages() {
329             // check if we should measure and record
330             long currentTimeMs = System.currentTimeMillis();
331             if ((currentTimeMs - mMeasurementsLastRecordedMs) < RECORD_MEASUREMENTS_COOLDOWN_MS) {
332                 Slog.d(TAG, "Skip measurement since the last measurement was only taken at "
333                         + mMeasurementsLastRecordedMs + " within the cooldown period");
334                 return;
335             }
336             Slog.d(TAG, "Measurement was last taken at " + mMeasurementsLastRecordedMs
337                     + " and is now updated to: " + currentTimeMs);
338             mMeasurementsLastRecordedMs = currentTimeMs;
339 
340             Bundle packagesMeasured = new Bundle();
341 
342             // measure all APEXs first
343             if (DEBUG) {
344                 Slog.d(TAG, "Measuring APEXs...");
345             }
346             List<IBinaryTransparencyService.ApexInfo> allApexInfo = collectAllApexInfo(
347                     /* includeTestOnly */ false);
348             for (IBinaryTransparencyService.ApexInfo apexInfo : allApexInfo) {
349                 packagesMeasured.putBoolean(apexInfo.packageName, true);
350 
351                 recordApexInfo(apexInfo);
352             }
353             if (DEBUG) {
354                 Slog.d(TAG, "Measured " + packagesMeasured.size()
355                         + " packages after considering APEXs.");
356             }
357 
358             if (!android.app.Flags.backgroundInstallControlCallbackApi()) {
359                 // proceed with all preloaded apps
360                 List<IBinaryTransparencyService.AppInfo> allUpdatedPreloadInfo =
361                         collectAllUpdatedPreloadInfo(packagesMeasured);
362                 for (IBinaryTransparencyService.AppInfo appInfo : allUpdatedPreloadInfo) {
363                     packagesMeasured.putBoolean(appInfo.packageName, true);
364                     writeAppInfoToLog(appInfo);
365                 }
366                 if (DEBUG) {
367                     Slog.d(TAG, "Measured " + packagesMeasured.size()
368                             + " packages after considering preloads");
369                 }
370 
371                 if (CompatChanges.isChangeEnabled(LOG_MBA_INFO)) {
372                     // lastly measure all newly installed MBAs
373                     List<IBinaryTransparencyService.AppInfo> allMbaInfo =
374                             collectAllSilentInstalledMbaInfo(packagesMeasured);
375                     for (IBinaryTransparencyService.AppInfo appInfo : allMbaInfo) {
376                         packagesMeasured.putBoolean(appInfo.packageName, true);
377                         writeAppInfoToLog(appInfo);
378                     }
379                 }
380             }
381             long timeSpentMeasuring = System.currentTimeMillis() - currentTimeMs;
382             digestAllPackagesLatency.logSample(timeSpentMeasuring);
383             if (DEBUG) {
384                 Slog.d(TAG, "Measured " + packagesMeasured.size()
385                         + " packages altogether in " + timeSpentMeasuring + "ms");
386             }
387         }
388 
389         @Override
collectAllApexInfo( boolean includeTestOnly)390         public List<IBinaryTransparencyService.ApexInfo> collectAllApexInfo(
391                 boolean includeTestOnly) {
392             var results = new ArrayList<IBinaryTransparencyService.ApexInfo>();
393             for (PackageInfo packageInfo : getCurrentInstalledApexs()) {
394                 PackageState packageState = mPackageManagerInternal.getPackageStateInternal(
395                         packageInfo.packageName);
396                 if (packageState == null) {
397                     Slog.w(TAG, "Package state is unavailable, ignoring the APEX "
398                             + packageInfo.packageName);
399                     continue;
400                 }
401 
402                 AndroidPackage pkg = packageState.getAndroidPackage();
403                 if (pkg == null) {
404                     Slog.w(TAG, "Skipping the missing APK in " + pkg.getPath());
405                     continue;
406                 }
407                 Digest apexChecksum = measureApk(pkg.getPath());
408                 if (apexChecksum == null) {
409                     Slog.w(TAG, "Skipping the missing APEX in " + pkg.getPath());
410                     continue;
411                 }
412 
413                 var apexInfo = new IBinaryTransparencyService.ApexInfo();
414                 apexInfo.packageName = packageState.getPackageName();
415                 apexInfo.longVersion = packageState.getVersionCode();
416                 apexInfo.digest = apexChecksum.value();
417                 apexInfo.digestAlgorithm = apexChecksum.algorithm();
418                 apexInfo.signerDigests =
419                         computePackageSignerSha256Digests(packageState.getSigningInfo());
420 
421                 if (includeTestOnly) {
422                     apexInfo.moduleName = apexPackageNameToModuleName(
423                             packageState.getPackageName());
424                 }
425 
426                 results.add(apexInfo);
427             }
428             return results;
429         }
430 
431         @Override
collectAllUpdatedPreloadInfo( Bundle packagesToSkip)432         public List<IBinaryTransparencyService.AppInfo> collectAllUpdatedPreloadInfo(
433                 Bundle packagesToSkip) {
434             final var results = new ArrayList<IBinaryTransparencyService.AppInfo>();
435 
436             PackageManager pm = mContext.getPackageManager();
437             mPackageManagerInternal.forEachPackageState((packageState) -> {
438                 if (!packageState.isUpdatedSystemApp()) {
439                     return;
440                 }
441                 if (packagesToSkip.containsKey(packageState.getPackageName())) {
442                     return;
443                 }
444 
445                 Slog.d(TAG, "Preload " + packageState.getPackageName() + " at "
446                         + packageState.getPath() + " has likely been updated.");
447 
448                 List<IBinaryTransparencyService.AppInfo> resultsForApp = collectAppInfo(
449                         packageState, MBA_STATUS_UPDATED_PRELOAD);
450                 results.addAll(resultsForApp);
451             });
452             return results;
453         }
454 
455         @Override
collectAllSilentInstalledMbaInfo( Bundle packagesToSkip)456         public List<IBinaryTransparencyService.AppInfo> collectAllSilentInstalledMbaInfo(
457                 Bundle packagesToSkip) {
458             var results = new ArrayList<IBinaryTransparencyService.AppInfo>();
459             for (PackageInfo packageInfo : getNewlyInstalledMbas()) {
460                 if (packagesToSkip.containsKey(packageInfo.packageName)) {
461                     continue;
462                 }
463                 PackageState packageState = mPackageManagerInternal.getPackageStateInternal(
464                         packageInfo.packageName);
465                 if (packageState == null) {
466                     Slog.w(TAG, "Package state is unavailable, ignoring the package "
467                             + packageInfo.packageName);
468                     continue;
469                 }
470 
471                 List<IBinaryTransparencyService.AppInfo> resultsForApp = collectAppInfo(
472                         packageState, MBA_STATUS_NEW_INSTALL);
473                 results.addAll(resultsForApp);
474             }
475             return results;
476         }
477 
recordApexInfo(IBinaryTransparencyService.ApexInfo apexInfo)478         private void recordApexInfo(IBinaryTransparencyService.ApexInfo apexInfo) {
479             // Must order by the proto's field number.
480             FrameworkStatsLog.write(FrameworkStatsLog.APEX_INFO_GATHERED,
481                     apexInfo.packageName,
482                     apexInfo.longVersion,
483                     (apexInfo.digest != null) ? HexEncoding.encodeToString(apexInfo.digest, false)
484                             : null,
485                     apexInfo.digestAlgorithm,
486                     apexInfo.signerDigests);
487         }
488 
489         @VisibleForTesting
writeAppInfoToLog(IBinaryTransparencyService.AppInfo appInfo)490         void writeAppInfoToLog(IBinaryTransparencyService.AppInfo appInfo) {
491             // Must order by the proto's field number.
492             FrameworkStatsLog.write(FrameworkStatsLog.MOBILE_BUNDLED_APP_INFO_GATHERED,
493                     appInfo.packageName,
494                     appInfo.longVersion,
495                     (appInfo.digest != null) ? HexEncoding.encodeToString(appInfo.digest, false)
496                             : null,
497                     appInfo.digestAlgorithm,
498                     appInfo.signerDigests,
499                     appInfo.mbaStatus,
500                     appInfo.initiator,
501                     appInfo.initiatorSignerDigests,
502                     appInfo.installer,
503                     appInfo.originator,
504                     appInfo.splitName);
505         }
506 
507         /**
508          * A wrapper around
509          * {@link ApkSignatureVerifier#verifySignaturesInternal(ParseInput, String, int, boolean)}.
510          * @param pathToApk The APK's installation path
511          * @return a {@code Map<Integer, byte[]>} with algorithm type as the key and content
512          *         digest as the value.
513          *         a {@code null} is returned upon encountering any error.
514          */
computeApkContentDigest(String pathToApk)515         private Map<Integer, byte[]> computeApkContentDigest(String pathToApk) {
516             final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
517             ParseResult<ApkSignatureVerifier.SigningDetailsWithDigests> parseResult =
518                     ApkSignatureVerifier.verifySignaturesInternal(input,
519                             pathToApk,
520                             SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2, false);
521             if (parseResult.isError()) {
522                 Slog.e(TAG, "Failed to compute content digest for "
523                         + pathToApk + " due to: "
524                         + parseResult.getErrorMessage());
525                 return null;
526             }
527             return parseResult.getResult().contentDigests;
528         }
529 
530         @Override
onShellCommand(@ullable FileDescriptor in, @Nullable FileDescriptor out, @Nullable FileDescriptor err, @NonNull String[] args, @Nullable ShellCallback callback, @NonNull ResultReceiver resultReceiver)531         public void onShellCommand(@Nullable FileDescriptor in,
532                                    @Nullable FileDescriptor out,
533                                    @Nullable FileDescriptor err,
534                                    @NonNull String[] args,
535                                    @Nullable ShellCallback callback,
536                                    @NonNull ResultReceiver resultReceiver) throws RemoteException {
537             (new ShellCommand() {
538 
539                 private int printSignedImageInfo() {
540                     final PrintWriter pw = getOutPrintWriter();
541                     boolean listAllPartitions = false;
542                     String opt;
543 
544                     while ((opt = getNextOption()) != null) {
545                         switch (opt) {
546                             case "-a":
547                                 listAllPartitions = true;
548                                 break;
549                             default:
550                                 pw.println("ERROR: Unknown option: " + opt);
551                                 return 1;
552                         }
553                     }
554 
555                     final String signedImageInfo = getSignedImageInfo();
556                     pw.println("Image Info:");
557                     pw.println(Build.FINGERPRINT);
558                     pw.println(signedImageInfo);
559                     pw.println("");
560 
561                     if (listAllPartitions) {
562                         PackageManager pm = mContext.getPackageManager();
563                         if (pm == null) {
564                             pw.println("ERROR: Failed to obtain an instance of package manager.");
565                             return -1;
566                         }
567 
568                         pw.println("Other partitions:");
569                         List<Build.Partition> buildPartitions = Build.getFingerprintedPartitions();
570                         for (Build.Partition buildPartition : buildPartitions) {
571                             pw.println("Name: " + buildPartition.getName());
572                             pw.println("Fingerprint: " + buildPartition.getFingerprint());
573                             pw.println("Build time (ms): " + buildPartition.getBuildTimeMillis());
574                         }
575                     }
576                     return 0;
577                 }
578 
579                 private void printPackageMeasurements(PackageInfo packageInfo,
580                                                       boolean useSha256,
581                                                       final PrintWriter pw) {
582                     Map<Integer, byte[]> contentDigests = computeApkContentDigest(
583                             packageInfo.applicationInfo.sourceDir);
584                     if (contentDigests == null) {
585                         pw.println("ERROR: Failed to compute package content digest for "
586                                 + packageInfo.applicationInfo.sourceDir);
587                         return;
588                     }
589 
590                     if (useSha256) {
591                         byte[] fileBuff = PackageUtils.createLargeFileBuffer();
592                         String hexEncodedSha256Digest =
593                                 PackageUtils.computeSha256DigestForLargeFile(
594                                         packageInfo.applicationInfo.sourceDir, fileBuff);
595                         pw.print(hexEncodedSha256Digest + ",");
596                     }
597 
598                     for (Map.Entry<Integer, byte[]> entry : contentDigests.entrySet()) {
599                         Integer algorithmId = entry.getKey();
600                         byte[] contentDigest = entry.getValue();
601 
602                         pw.print(translateContentDigestAlgorithmIdToString(algorithmId));
603                         pw.print(":");
604                         pw.print(HexEncoding.encodeToString(contentDigest, false));
605                         pw.print("\n");
606                     }
607                 }
608 
609                 private void printPackageInstallationInfo(PackageInfo packageInfo,
610                                                           boolean useSha256,
611                                                           final PrintWriter pw) {
612                     pw.println("--- Package Installation Info ---");
613                     pw.println("Current install location: "
614                             + packageInfo.applicationInfo.sourceDir);
615                     if (packageInfo.applicationInfo.sourceDir.startsWith("/data/apex/")) {
616                         String origPackageFilepath = getOriginalApexPreinstalledLocation(
617                                 packageInfo.packageName);
618                         pw.println("|--> Pre-installed package install location: "
619                                 + origPackageFilepath);
620 
621                         if (!origPackageFilepath.equals(APEX_PRELOAD_LOCATION_ERROR)) {
622                             if (useSha256) {
623                                 String sha256Digest = PackageUtils.computeSha256DigestForLargeFile(
624                                         origPackageFilepath, PackageUtils.createLargeFileBuffer());
625                                 pw.println("|--> Pre-installed package SHA-256 digest: "
626                                         + sha256Digest);
627                             }
628 
629                             Map<Integer, byte[]> contentDigests = computeApkContentDigest(
630                                     origPackageFilepath);
631                             if (contentDigests == null) {
632                                 pw.println("|--> ERROR: Failed to compute package content digest "
633                                         + "for " + origPackageFilepath);
634                             } else {
635                                 for (Map.Entry<Integer, byte[]> entry : contentDigests.entrySet()) {
636                                     Integer algorithmId = entry.getKey();
637                                     byte[] contentDigest = entry.getValue();
638                                     pw.println("|--> Pre-installed package content digest: "
639                                             + HexEncoding.encodeToString(contentDigest, false));
640                                     pw.println("|--> Pre-installed package content digest "
641                                             + "algorithm: "
642                                             + translateContentDigestAlgorithmIdToString(
643                                                     algorithmId));
644                                 }
645                             }
646                         }
647                     }
648                     pw.println("First install time (ms): " + packageInfo.firstInstallTime);
649                     pw.println("Last update time (ms):   " + packageInfo.lastUpdateTime);
650                     // TODO(b/261493591): Determination of whether a package is preinstalled can be
651                     // made more robust
652                     boolean isPreloaded = (packageInfo.firstInstallTime
653                             == packageInfo.lastUpdateTime);
654                     pw.println("Is preloaded: " + isPreloaded);
655 
656                     InstallSourceInfo installSourceInfo = getInstallSourceInfo(
657                             packageInfo.packageName);
658                     if (installSourceInfo == null) {
659                         pw.println("ERROR: Unable to obtain installSourceInfo of "
660                                 + packageInfo.packageName);
661                     } else {
662                         pw.println("Installation initiated by: "
663                                 + installSourceInfo.getInitiatingPackageName());
664                         pw.println("Installation done by: "
665                                 + installSourceInfo.getInstallingPackageName());
666                         pw.println("Installation originating from: "
667                                 + installSourceInfo.getOriginatingPackageName());
668                     }
669 
670                     if (packageInfo.isApex) {
671                         pw.println("Is an active APEX: " + packageInfo.isActiveApex);
672                     }
673                 }
674 
675                 private void printPackageSignerDetails(SigningInfo signerInfo,
676                                                        final PrintWriter pw) {
677                     if (signerInfo == null) {
678                         pw.println("ERROR: Package's signingInfo is null.");
679                         return;
680                     }
681                     pw.println("--- Package Signer Info ---");
682                     pw.println("Has multiple signers: " + signerInfo.hasMultipleSigners());
683                     pw.println("Signing key has been rotated: "
684                             + signerInfo.hasPastSigningCertificates());
685                     Signature[] packageSigners = signerInfo.getApkContentsSigners();
686                     for (Signature packageSigner : packageSigners) {
687                         byte[] packageSignerDigestBytes =
688                                 PackageUtils.computeSha256DigestBytes(packageSigner.toByteArray());
689                         String packageSignerDigestHextring =
690                                 HexEncoding.encodeToString(packageSignerDigestBytes, false);
691                         pw.println("Signer cert's SHA256-digest: " + packageSignerDigestHextring);
692                         try {
693                             PublicKey publicKey = packageSigner.getPublicKey();
694                             pw.println("Signing key algorithm: " + publicKey.getAlgorithm());
695                         } catch (CertificateException e) {
696                             Slog.e(TAG,
697                                     "Failed to obtain public key of signer for cert with hash: "
698                                     + packageSignerDigestHextring, e);
699                         }
700                     }
701 
702                     if (!signerInfo.hasMultipleSigners()
703                             && signerInfo.hasPastSigningCertificates()) {
704                         pw.println("== Signing Cert Lineage (Excluding The Most Recent) ==");
705                         pw.println("(Certs are sorted in the order of rotation, beginning with the "
706                                    + "original signing cert)");
707                         Signature[] signingCertHistory = signerInfo.getSigningCertificateHistory();
708                         for (int i = 0; i < (signingCertHistory.length - 1); i++) {
709                             Signature signature = signingCertHistory[i];
710                             byte[] signatureDigestBytes = PackageUtils.computeSha256DigestBytes(
711                                     signature.toByteArray());
712                             String certHashHexString = HexEncoding.encodeToString(
713                                     signatureDigestBytes, false);
714                             pw.println("  ++ Signer cert #" + (i + 1) + " ++");
715                             pw.println("  Cert SHA256-digest: " + certHashHexString);
716                             try {
717                                 PublicKey publicKey = signature.getPublicKey();
718                                 pw.println("  Signing key algorithm: " + publicKey.getAlgorithm());
719                             } catch (CertificateException e) {
720                                 Slog.e(TAG, "Failed to obtain public key of signer for cert "
721                                         + "with hash: " + certHashHexString, e);
722                             }
723                         }
724                     }
725 
726                 }
727 
728                 private void printModuleDetails(ModuleInfo moduleInfo, final PrintWriter pw) {
729                     pw.println("--- Module Details ---");
730                     pw.println("Module name: " + moduleInfo.getName());
731                     if (!android.content.pm.Flags.removeHiddenModuleUsage()) {
732                         pw.println("Module visibility: "
733                         + (moduleInfo.isHidden() ? "hidden" : "visible"));
734                     }
735                 }
736 
737                 private void printAppDetails(PackageInfo packageInfo,
738                                              boolean printLibraries,
739                                              final PrintWriter pw) {
740                     pw.println("--- App Details ---");
741                     pw.println("Name: " + packageInfo.applicationInfo.name);
742                     pw.println("Label: " + mContext.getPackageManager().getApplicationLabel(
743                             packageInfo.applicationInfo));
744                     pw.println("Description: " + packageInfo.applicationInfo.loadDescription(
745                             mContext.getPackageManager()));
746                     pw.println("Has code: " + packageInfo.applicationInfo.hasCode());
747                     pw.println("Is enabled: " + packageInfo.applicationInfo.enabled);
748                     pw.println("Is suspended: " + ((packageInfo.applicationInfo.flags
749                                                     & ApplicationInfo.FLAG_SUSPENDED) != 0));
750 
751                     pw.println("Compile SDK version: " + packageInfo.compileSdkVersion);
752                     pw.println("Target SDK version: "
753                             + packageInfo.applicationInfo.targetSdkVersion);
754 
755                     pw.println("Is privileged: "
756                             + packageInfo.applicationInfo.isPrivilegedApp());
757                     pw.println("Is a stub: " + packageInfo.isStub);
758                     pw.println("Is a core app: " + packageInfo.coreApp);
759                     pw.println("SEInfo: " + packageInfo.applicationInfo.seInfo);
760                     pw.println("Component factory: "
761                             + packageInfo.applicationInfo.appComponentFactory);
762                     pw.println("Process name: " + packageInfo.applicationInfo.processName);
763                     pw.println("Task affinity: " + packageInfo.applicationInfo.taskAffinity);
764                     pw.println("UID: " + packageInfo.applicationInfo.uid);
765                     pw.println("Shared UID: " + packageInfo.sharedUserId);
766 
767                     if (printLibraries) {
768                         pw.println("== App's Shared Libraries ==");
769                         List<SharedLibraryInfo> sharedLibraryInfos =
770                                 packageInfo.applicationInfo.getSharedLibraryInfos();
771                         if (sharedLibraryInfos == null || sharedLibraryInfos.isEmpty()) {
772                             pw.println("<none>");
773                         }
774 
775                         for (int i = 0; i < sharedLibraryInfos.size(); i++) {
776                             SharedLibraryInfo sharedLibraryInfo = sharedLibraryInfos.get(i);
777                             pw.println("  ++ Library #" + (i + 1) + " ++");
778                             pw.println("  Lib name: " + sharedLibraryInfo.getName());
779                             long libVersion = sharedLibraryInfo.getLongVersion();
780                             pw.print("  Lib version: ");
781                             if (libVersion == SharedLibraryInfo.VERSION_UNDEFINED) {
782                                 pw.print("undefined");
783                             } else {
784                                 pw.print(libVersion);
785                             }
786                             pw.print("\n");
787 
788                             pw.println("  Lib package name (if available): "
789                                     + sharedLibraryInfo.getPackageName());
790                             pw.println("  Lib path: " + sharedLibraryInfo.getPath());
791                             pw.print("  Lib type: ");
792                             switch (sharedLibraryInfo.getType()) {
793                                 case SharedLibraryInfo.TYPE_BUILTIN:
794                                     pw.print("built-in");
795                                     break;
796                                 case SharedLibraryInfo.TYPE_DYNAMIC:
797                                     pw.print("dynamic");
798                                     break;
799                                 case SharedLibraryInfo.TYPE_STATIC:
800                                     pw.print("static");
801                                     break;
802                                 case SharedLibraryInfo.TYPE_SDK_PACKAGE:
803                                     pw.print("SDK");
804                                     break;
805                                 case SharedLibraryInfo.VERSION_UNDEFINED:
806                                 default:
807                                     pw.print("undefined");
808                                     break;
809                             }
810                             pw.print("\n");
811                             pw.println("  Is a native lib: " + sharedLibraryInfo.isNative());
812                         }
813                     }
814 
815                 }
816 
817                 private void printHeadersHelper(@NonNull String packageType,
818                                           boolean useSha256,
819                                           @NonNull final PrintWriter pw) {
820                     pw.print(packageType + " Info [Format: package_name,package_version,");
821                     if (useSha256) {
822                         pw.print("package_sha256_digest,");
823                     }
824                     pw.print("content_digest_algorithm:content_digest]:\n");
825                 }
826 
827                 private int printAllApexs() {
828                     final PrintWriter pw = getOutPrintWriter();
829                     boolean verbose = false;
830                     boolean useSha256 = false;
831                     boolean printHeaders = true;
832                     String opt;
833                     while ((opt = getNextOption()) != null) {
834                         switch (opt) {
835                             case "-v":
836                             case "--verbose":
837                                 verbose = true;
838                                 break;
839                             case "-o":
840                             case "--old":
841                                 useSha256 = true;
842                                 break;
843                             case "--no-headers":
844                                 printHeaders = false;
845                                 break;
846                             default:
847                                 pw.println("ERROR: Unknown option: " + opt);
848                                 return 1;
849                         }
850                     }
851 
852                     PackageManager pm = mContext.getPackageManager();
853                     if (pm == null) {
854                         pw.println("ERROR: Failed to obtain an instance of package manager.");
855                         return -1;
856                     }
857 
858                     if (!verbose && printHeaders) {
859                         printHeadersHelper("APEX", useSha256, pw);
860                     }
861                     for (PackageInfo packageInfo : getCurrentInstalledApexs()) {
862                         if (verbose && printHeaders) {
863                             printHeadersHelper("APEX", useSha256, pw);
864                         }
865                         String packageName = packageInfo.packageName;
866                         pw.print(packageName + ","
867                                 + packageInfo.getLongVersionCode() + ",");
868                         printPackageMeasurements(packageInfo, useSha256, pw);
869 
870                         if (verbose) {
871                             ModuleInfo moduleInfo;
872                             try {
873                                 moduleInfo = pm.getModuleInfo(packageInfo.packageName, 0);
874                                 pw.println("Is a module: true");
875                                 printModuleDetails(moduleInfo, pw);
876                             } catch (PackageManager.NameNotFoundException e) {
877                                 pw.println("Is a module: false");
878                             }
879 
880                             printPackageInstallationInfo(packageInfo, useSha256, pw);
881                             printPackageSignerDetails(packageInfo.signingInfo, pw);
882                             pw.println("");
883                         }
884                     }
885                     return 0;
886                 }
887 
888                 private int printAllModules() {
889                     final PrintWriter pw = getOutPrintWriter();
890                     boolean verbose = false;
891                     boolean useSha256 = false;
892                     boolean printHeaders = true;
893                     String opt;
894                     while ((opt = getNextOption()) != null) {
895                         switch (opt) {
896                             case "-v":
897                             case "--verbose":
898                                 verbose = true;
899                                 break;
900                             case "-o":
901                             case "--old":
902                                 useSha256 = true;
903                                 break;
904                             case "--no-headers":
905                                 printHeaders = false;
906                                 break;
907                             default:
908                                 pw.println("ERROR: Unknown option: " + opt);
909                                 return 1;
910                         }
911                     }
912 
913                     PackageManager pm = mContext.getPackageManager();
914                     if (pm == null) {
915                         pw.println("ERROR: Failed to obtain an instance of package manager.");
916                         return -1;
917                     }
918 
919                     if (!verbose && printHeaders) {
920                         printHeadersHelper("Module", useSha256, pw);
921                     }
922                     for (ModuleInfo module : pm.getInstalledModules(PackageManager.MATCH_ALL)) {
923                         String packageName = module.getPackageName();
924                         if (verbose && printHeaders) {
925                             printHeadersHelper("Module", useSha256, pw);
926                         }
927                         try {
928                             PackageInfo packageInfo = pm.getPackageInfo(packageName,
929                                     PackageManager.MATCH_APEX
930                                             | PackageManager.GET_SIGNING_CERTIFICATES);
931                             pw.print(packageInfo.packageName + ",");
932                             pw.print(packageInfo.getLongVersionCode() + ",");
933                             printPackageMeasurements(packageInfo, useSha256, pw);
934 
935                             if (verbose) {
936                                 printModuleDetails(module, pw);
937                                 printPackageInstallationInfo(packageInfo, useSha256, pw);
938                                 printPackageSignerDetails(packageInfo.signingInfo, pw);
939                                 pw.println("");
940                             }
941                         } catch (PackageManager.NameNotFoundException e) {
942                             pw.println(packageName
943                                     + ",ERROR:Unable to find PackageInfo for this module.");
944                             if (verbose) {
945                                 printModuleDetails(module, pw);
946                                 pw.println("");
947                             }
948                             continue;
949                         }
950                     }
951                     return 0;
952                 }
953 
954                 private int printAllMbas() {
955                     final PrintWriter pw = getOutPrintWriter();
956                     boolean verbose = false;
957                     boolean printLibraries = false;
958                     boolean useSha256 = false;
959                     boolean printHeaders = true;
960                     boolean preloadsOnly = false;
961                     String opt;
962                     while ((opt = getNextOption()) != null) {
963                         switch (opt) {
964                             case "-v":
965                             case "--verbose":
966                                 verbose = true;
967                                 break;
968                             case "-l":
969                                 printLibraries = true;
970                                 break;
971                             case "-o":
972                             case "--old":
973                                 useSha256 = true;
974                                 break;
975                             case "--no-headers":
976                                 printHeaders = false;
977                                 break;
978                             case "--preloads-only":
979                                 preloadsOnly = true;
980                                 break;
981                             default:
982                                 pw.println("ERROR: Unknown option: " + opt);
983                                 return 1;
984                         }
985                     }
986 
987                     if (!verbose && printHeaders) {
988                         if (preloadsOnly) {
989                             printHeadersHelper("Preload", useSha256, pw);
990                         } else {
991                             printHeadersHelper("MBA", useSha256, pw);
992                         }
993                     }
994 
995                     PackageManager pm = mContext.getPackageManager();
996                     for (PackageInfo packageInfo : pm.getInstalledPackages(
997                             PackageManager.PackageInfoFlags.of(PackageManager.MATCH_FACTORY_ONLY
998                             | PackageManager.GET_SIGNING_CERTIFICATES))) {
999                         if (packageInfo.signingInfo == null) {
1000                             PackageInfo origPackageInfo = packageInfo;
1001                             try {
1002                                 pm.getPackageInfo(packageInfo.packageName,
1003                                         PackageManager.PackageInfoFlags.of(PackageManager.MATCH_ALL
1004                                                 | PackageManager.GET_SIGNING_CERTIFICATES));
1005                             } catch (PackageManager.NameNotFoundException e) {
1006                                 Slog.e(TAG, "Failed to obtain an updated PackageInfo of "
1007                                         + origPackageInfo.packageName);
1008                                 packageInfo = origPackageInfo;
1009                             }
1010                         }
1011 
1012                         if (verbose && printHeaders) {
1013                             printHeadersHelper("Preload", useSha256, pw);
1014                         }
1015                         pw.print(packageInfo.packageName + ",");
1016                         pw.print(packageInfo.getLongVersionCode() + ",");
1017                         printPackageMeasurements(packageInfo, useSha256, pw);
1018 
1019                         if (verbose) {
1020                             printAppDetails(packageInfo, printLibraries, pw);
1021                             printPackageInstallationInfo(packageInfo, useSha256, pw);
1022                             printPackageSignerDetails(packageInfo.signingInfo, pw);
1023                             pw.println("");
1024                         }
1025                     }
1026 
1027                     if (preloadsOnly) {
1028                         return 0;
1029                     }
1030                     for (PackageInfo packageInfo : getNewlyInstalledMbas()) {
1031                         if (verbose && printHeaders) {
1032                             printHeadersHelper("MBA", useSha256, pw);
1033                         }
1034                         pw.print(packageInfo.packageName + ",");
1035                         pw.print(packageInfo.getLongVersionCode() + ",");
1036                         printPackageMeasurements(packageInfo, useSha256, pw);
1037 
1038                         if (verbose) {
1039                             printAppDetails(packageInfo, printLibraries, pw);
1040                             printPackageInstallationInfo(packageInfo, useSha256, pw);
1041                             printPackageSignerDetails(packageInfo.signingInfo, pw);
1042                             pw.println("");
1043                         }
1044                     }
1045                     return 0;
1046                 }
1047 
1048                 @Override
1049                 public int onCommand(String cmd) {
1050                     if (cmd == null) {
1051                         return handleDefaultCommands(cmd);
1052                     }
1053 
1054                     final PrintWriter pw = getOutPrintWriter();
1055                     switch (cmd) {
1056                         case "get": {
1057                             final String infoType = getNextArg();
1058                             if (infoType == null) {
1059                                 printHelpMenu();
1060                                 return -1;
1061                             }
1062 
1063                             switch (infoType) {
1064                                 case "image_info":
1065                                     return printSignedImageInfo();
1066                                 case "apex_info":
1067                                     return printAllApexs();
1068                                 case "module_info":
1069                                     return printAllModules();
1070                                 case "mba_info":
1071                                     return printAllMbas();
1072                                 default:
1073                                     pw.println(String.format("ERROR: Unknown info type '%s'",
1074                                             infoType));
1075                                     return 1;
1076                             }
1077                         }
1078                         default:
1079                             return handleDefaultCommands(cmd);
1080                     }
1081                 }
1082 
1083                 private void printHelpMenu() {
1084                     final PrintWriter pw = getOutPrintWriter();
1085                     pw.println("Transparency manager (transparency) commands:");
1086                     pw.println("  help");
1087                     pw.println("    Print this help text.");
1088                     pw.println("");
1089                     pw.println("  get image_info [-a]");
1090                     pw.println("    Print information about loaded image (firmware). Options:");
1091                     pw.println("        -a: lists all other identifiable partitions.");
1092                     pw.println("");
1093                     pw.println("  get apex_info [-o] [-v] [--no-headers]");
1094                     pw.println("    Print information about installed APEXs on device.");
1095                     pw.println("      -o: also uses the old digest scheme (SHA256) to compute "
1096                                + "APEX hashes. WARNING: This can be a very slow and CPU-intensive "
1097                                + "computation.");
1098                     pw.println("      -v: lists more verbose information about each APEX.");
1099                     pw.println("      --no-headers: does not print the header if specified.");
1100                     pw.println("");
1101                     pw.println("  get module_info [-o] [-v] [--no-headers]");
1102                     pw.println("    Print information about installed modules on device.");
1103                     pw.println("      -o: also uses the old digest scheme (SHA256) to compute "
1104                                + "module hashes. WARNING: This can be a very slow and "
1105                                + "CPU-intensive computation.");
1106                     pw.println("      -v: lists more verbose information about each module.");
1107                     pw.println("      --no-headers: does not print the header if specified.");
1108                     pw.println("");
1109                     pw.println("  get mba_info [-o] [-v] [-l] [--no-headers] [--preloads-only]");
1110                     pw.println("    Print information about installed mobile bundle apps "
1111                                + "(MBAs on device).");
1112                     pw.println("      -o: also uses the old digest scheme (SHA256) to compute "
1113                                + "MBA hashes. WARNING: This can be a very slow and CPU-intensive "
1114                                + "computation.");
1115                     pw.println("      -v: lists more verbose information about each app.");
1116                     pw.println("      -l: lists shared library info. (This option only works "
1117                                + "when -v option is also specified)");
1118                     pw.println("      --no-headers: does not print the header if specified.");
1119                     pw.println("      --preloads-only: lists only preloaded apps. This options can "
1120                                + "also be combined with others.");
1121                     pw.println("");
1122                 }
1123 
1124                 @Override
1125                 public void onHelp() {
1126                     printHelpMenu();
1127                 }
1128             }).exec(this, in, out, err, args, callback, resultReceiver);
1129         }
1130     }
1131     private final BinaryTransparencyServiceImpl mServiceImpl;
1132 
1133     /**
1134      * A wrapper of {@link FrameworkStatsLog} for easier testing
1135      */
1136     @VisibleForTesting
1137     public static class BiometricLogger {
1138         private static final String TAG = "BiometricLogger";
1139 
1140         private static final BiometricLogger sInstance = new BiometricLogger();
1141 
BiometricLogger()1142         private BiometricLogger() {}
1143 
getInstance()1144         public static BiometricLogger getInstance() {
1145             return sInstance;
1146         }
1147 
1148         /**
1149          * A wrapper of {@link FrameworkStatsLog}
1150          *
1151          * @param sensorId The sensorId of the biometric to be logged
1152          * @param modality The modality of the biometric
1153          * @param sensorType The sensor type of the biometric
1154          * @param sensorStrength The sensor strength of the biometric
1155          * @param componentId The component Id of a component of the biometric
1156          * @param hardwareVersion The hardware version of a component of the biometric
1157          * @param firmwareVersion The firmware version of a component of the biometric
1158          * @param serialNumber The serial number of a component of the biometric
1159          * @param softwareVersion The software version of a component of the biometric
1160          */
logStats(int sensorId, int modality, int sensorType, int sensorStrength, String componentId, String hardwareVersion, String firmwareVersion, String serialNumber, String softwareVersion)1161         public void logStats(int sensorId, int modality, int sensorType, int sensorStrength,
1162                 String componentId, String hardwareVersion, String firmwareVersion,
1163                 String serialNumber, String softwareVersion) {
1164             FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_PROPERTIES_COLLECTED,
1165                     sensorId, modality, sensorType, sensorStrength, componentId, hardwareVersion,
1166                     firmwareVersion, serialNumber, softwareVersion);
1167         }
1168     }
1169 
BinaryTransparencyService(Context context)1170     public BinaryTransparencyService(Context context) {
1171         this(context, BiometricLogger.getInstance());
1172     }
1173 
1174     @VisibleForTesting
BinaryTransparencyService(Context context, BiometricLogger biometricLogger)1175     BinaryTransparencyService(Context context, BiometricLogger biometricLogger) {
1176         super(context);
1177         mContext = context;
1178         mServiceImpl = new BinaryTransparencyServiceImpl();
1179         mVbmetaDigest = VBMETA_DIGEST_UNINITIALIZED;
1180         mMeasurementsLastRecordedMs = 0;
1181         mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
1182         mBiometricLogger = biometricLogger;
1183     }
1184 
1185     /**
1186      * Receive callbacks from BIC to write silently installed apps to log
1187      *
1188      * TODO: Add a host test for testing registration and callback of BicCallbackHandler
1189      *  b/380002484
1190      */
1191     @VisibleForTesting
1192     static class BicCallbackHandler extends IRemoteCallback.Stub {
1193         private static final String BIC_CALLBACK_HANDLER_TAG = TAG + ".BicCallbackHandler";
1194 
1195         private static final int INSTALL_EVENT_TYPE_UNSET = -1;
1196 
1197         private final IBicAppInfoHelper mBicAppInfoHelper;
1198 
1199         @VisibleForTesting
BicCallbackHandler(IBicAppInfoHelper bicAppInfoHelper)1200         BicCallbackHandler(IBicAppInfoHelper bicAppInfoHelper) {
1201             mBicAppInfoHelper = bicAppInfoHelper;
1202         }
1203 
1204         @Override
sendResult(Bundle data)1205         public void sendResult(Bundle data) {
1206             String packageName = data.getString(
1207                     BackgroundInstallControlCallbackHelper.FLAGGED_PACKAGE_NAME_KEY);
1208             int installType = data.getInt(
1209                     BackgroundInstallControlCallbackHelper.INSTALL_EVENT_TYPE_KEY,
1210                     INSTALL_EVENT_TYPE_UNSET);
1211             if (packageName == null || installType == INSTALL_EVENT_TYPE_UNSET) {
1212                 Slog.w(BIC_CALLBACK_HANDLER_TAG, "Package name or install type is "
1213                         + "unavailable, ignoring event");
1214                 return;
1215             }
1216             Slog.d(BIC_CALLBACK_HANDLER_TAG, "Detected new bic event for: " + packageName);
1217             if (installType == BackgroundInstallControlService.INSTALL_EVENT_TYPE_INSTALL) {
1218                 PackageState packageState = LocalServices.getService(PackageManagerInternal.class)
1219                     .getPackageStateInternal(packageName);
1220                 if (packageState == null) {
1221                     Slog.w(TAG, "Package state is unavailable, ignoring the package "
1222                             + packageName);
1223                     return;
1224                 }
1225                 int mbaStatus = MBA_STATUS_NEW_INSTALL;
1226                 if (packageState.isUpdatedSystemApp()) {
1227                     mbaStatus = MBA_STATUS_UPDATED_PRELOAD;
1228                 }
1229                 List<IBinaryTransparencyService.AppInfo> mbaInfo = mBicAppInfoHelper.collectAppInfo(
1230                         packageState, mbaStatus);
1231                 for (IBinaryTransparencyService.AppInfo appInfo : mbaInfo) {
1232                     mBicAppInfoHelper.writeAppInfoToLog(appInfo);
1233                 }
1234             } else if (installType
1235                         == BackgroundInstallControlService.INSTALL_EVENT_TYPE_UNINSTALL) {
1236                 IBinaryTransparencyService.AppInfo appInfo
1237                     = new IBinaryTransparencyService.AppInfo();
1238                 // since app is already uninstalled we won't be able to retrieve additional
1239                 // info on it.
1240                 appInfo.packageName = packageName;
1241                 appInfo.mbaStatus = MBA_STATUS_UNINSTALLED;
1242                 mBicAppInfoHelper.writeAppInfoToLog(appInfo);
1243             } else {
1244                 Slog.w(BIC_CALLBACK_HANDLER_TAG, "Unsupported BIC event: " + installType);
1245             }
1246         }
1247 
1248         /**
1249          * A wrapper of interface for{@link FrameworkStatsLog and ApkDigests}
1250          * for easier testing
1251          */
1252         @VisibleForTesting
1253         public interface IBicAppInfoHelper {
1254 
1255             /**
1256              * A wrapper of {@link FrameworkStatsLog}
1257              *
1258              * @param appInfo The app info of the changed MBA to be logged
1259              */
writeAppInfoToLog(IBinaryTransparencyService.AppInfo appInfo)1260             public void writeAppInfoToLog(IBinaryTransparencyService.AppInfo appInfo);
1261 
1262             /**
1263              * A wrapper of {@link BinaryTransparencyServiceImpl}
1264              *
1265              * @param packageState The packageState provided retrieved from PackageManagerInternal
1266              * @param mbaStatus The MBA status of the package
1267              */
collectAppInfo( PackageState packageState, int mbaStatus)1268             public List<IBinaryTransparencyService.AppInfo> collectAppInfo(
1269                 PackageState packageState, int mbaStatus);
1270         }
1271     };
1272 
1273     /**
1274      * Called when the system service should publish a binder service using
1275      * {@link #publishBinderService(String, IBinder).}
1276      */
1277     @Override
onStart()1278     public void onStart() {
1279         try {
1280             publishBinderService(Context.BINARY_TRANSPARENCY_SERVICE, mServiceImpl);
1281             Slog.i(TAG, "Started BinaryTransparencyService");
1282         } catch (Throwable t) {
1283             Slog.e(TAG, "Failed to start BinaryTransparencyService.", t);
1284         }
1285     }
1286 
1287     /**
1288      * Called on each phase of the boot process. Phases before the service's start phase
1289      * (as defined in the @Service annotation) are never received.
1290      *
1291      * @param phase The current boot phase.
1292      */
1293     @Override
onBootPhase(int phase)1294     public void onBootPhase(int phase) {
1295 
1296         // we are only interested in doing things at PHASE_BOOT_COMPLETED
1297         if (phase == PHASE_BOOT_COMPLETED) {
1298             Slog.i(TAG, "Boot completed. Getting boot integrity data.");
1299             collectBootIntegrityInfo();
1300 
1301             // Log to statsd
1302             // TODO(b/264061957): For now, biometric system properties are always collected if users
1303             //  share usage & diagnostics information. In the future, collect biometric system
1304             //  properties only when transparency log verification of the target partitions fails
1305             //  (e.g. when the system/vendor partitions have been changed) once the binary
1306             //  transparency infrastructure is ready.
1307             Slog.i(TAG, "Boot completed. Collecting biometric system properties.");
1308             collectBiometricProperties();
1309 
1310             // to avoid the risk of holding up boot time, computations to measure APEX, Module, and
1311             // MBA digests are scheduled here, but only executed when the device is idle and plugged
1312             // in.
1313             Slog.i(TAG, "Scheduling measurements to be taken.");
1314             UpdateMeasurementsJobService.scheduleBinaryMeasurements(mContext,
1315                     BinaryTransparencyService.this);
1316 
1317             registerAllPackageUpdateObservers();
1318         }
1319     }
1320 
1321     /**
1322      * JobService to measure all covered binaries and record results to statsd.
1323      */
1324     public static class UpdateMeasurementsJobService extends JobService {
1325         private static long sTimeLastRanMs = 0;
1326         private static final int DO_BINARY_MEASUREMENTS_JOB_ID = 1740526926;
1327 
1328         @Override
onStartJob(JobParameters params)1329         public boolean onStartJob(JobParameters params) {
1330             Slog.d(TAG, "Job to update binary measurements started.");
1331             if (params.getJobId() != DO_BINARY_MEASUREMENTS_JOB_ID) {
1332                 return false;
1333             }
1334 
1335             // we'll perform binary measurements via threads to be mindful of low-end devices
1336             // where this operation might take longer than expected, and so that we don't block
1337             // system_server's main thread.
1338             Executors.defaultThreadFactory().newThread(() -> {
1339                 IBinder b = ServiceManager.getService(Context.BINARY_TRANSPARENCY_SERVICE);
1340                 IBinaryTransparencyService iBtsService =
1341                         IBinaryTransparencyService.Stub.asInterface(b);
1342                 try {
1343                     iBtsService.recordMeasurementsForAllPackages();
1344                 } catch (RemoteException e) {
1345                     Slog.e(TAG, "Taking binary measurements was interrupted.", e);
1346                     return;
1347                 }
1348                 sTimeLastRanMs = System.currentTimeMillis();
1349                 jobFinished(params, false);
1350             }).start();
1351 
1352             return true;
1353         }
1354 
1355         @Override
onStopJob(JobParameters params)1356         public boolean onStopJob(JobParameters params) {
1357             return false;
1358         }
1359 
1360         @SuppressLint("DefaultLocale")
scheduleBinaryMeasurements(Context context, BinaryTransparencyService service)1361         static void scheduleBinaryMeasurements(Context context, BinaryTransparencyService service) {
1362             Slog.i(TAG, "Scheduling binary content-digest computation job");
1363             final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
1364             if (jobScheduler == null) {
1365                 Slog.e(TAG, "Failed to obtain an instance of JobScheduler.");
1366                 return;
1367             }
1368 
1369             if (jobScheduler.getPendingJob(DO_BINARY_MEASUREMENTS_JOB_ID) != null) {
1370                 Slog.d(TAG, "A measurement job has already been scheduled.");
1371                 return;
1372             }
1373 
1374             long minWaitingPeriodMs = 0;
1375             if (sTimeLastRanMs != 0) {
1376                 minWaitingPeriodMs = RECORD_MEASUREMENTS_COOLDOWN_MS
1377                         - (System.currentTimeMillis() - sTimeLastRanMs);
1378                 // bound the range of minWaitingPeriodMs in the case where > 24h has elapsed
1379                 minWaitingPeriodMs = Math.max(0,
1380                         Math.min(minWaitingPeriodMs, RECORD_MEASUREMENTS_COOLDOWN_MS));
1381                 Slog.d(TAG, "Scheduling the next measurement to be done at least "
1382                         + minWaitingPeriodMs + "ms from now.");
1383             }
1384 
1385             final JobInfo jobInfo = new JobInfo.Builder(DO_BINARY_MEASUREMENTS_JOB_ID,
1386                     new ComponentName(context, UpdateMeasurementsJobService.class))
1387                     .setRequiresDeviceIdle(true)
1388                     .setRequiresCharging(true)
1389                     .setMinimumLatency(minWaitingPeriodMs)
1390                     .build();
1391             if (jobScheduler.schedule(jobInfo) != JobScheduler.RESULT_SUCCESS) {
1392                 Slog.e(TAG, "Failed to schedule job to measure binaries.");
1393                 return;
1394             }
1395             Slog.d(TAG, TextUtils.formatSimple(
1396                     "Job %d to measure binaries was scheduled successfully.",
1397                     DO_BINARY_MEASUREMENTS_JOB_ID));
1398         }
1399     }
1400 
1401     /**
1402      * Convert a {@link FingerprintSensorProperties} sensor type to the corresponding enum to be
1403      * logged.
1404      *
1405      * @param sensorType See {@link FingerprintSensorProperties}
1406      * @return The enum to be logged
1407      */
toFingerprintSensorType(@ingerprintSensorProperties.SensorType int sensorType)1408     private int toFingerprintSensorType(@FingerprintSensorProperties.SensorType int sensorType) {
1409         switch (sensorType) {
1410             case FingerprintSensorProperties.TYPE_REAR:
1411                 return FrameworkStatsLog
1412                         .BIOMETRIC_PROPERTIES_COLLECTED__SENSOR_TYPE__SENSOR_FP_REAR;
1413             case FingerprintSensorProperties.TYPE_UDFPS_ULTRASONIC:
1414                 return FrameworkStatsLog
1415                         .BIOMETRIC_PROPERTIES_COLLECTED__SENSOR_TYPE__SENSOR_FP_UDFPS_ULTRASONIC;
1416             case FingerprintSensorProperties.TYPE_UDFPS_OPTICAL:
1417                 return FrameworkStatsLog
1418                         .BIOMETRIC_PROPERTIES_COLLECTED__SENSOR_TYPE__SENSOR_FP_UDFPS_OPTICAL;
1419             case FingerprintSensorProperties.TYPE_POWER_BUTTON:
1420                 return FrameworkStatsLog
1421                         .BIOMETRIC_PROPERTIES_COLLECTED__SENSOR_TYPE__SENSOR_FP_POWER_BUTTON;
1422             case FingerprintSensorProperties.TYPE_HOME_BUTTON:
1423                 return FrameworkStatsLog
1424                         .BIOMETRIC_PROPERTIES_COLLECTED__SENSOR_TYPE__SENSOR_FP_HOME_BUTTON;
1425             default:
1426                 return FrameworkStatsLog
1427                         .BIOMETRIC_PROPERTIES_COLLECTED__SENSOR_TYPE__SENSOR_UNKNOWN;
1428         }
1429     }
1430 
1431     /**
1432      * Convert a {@link FaceSensorProperties} sensor type to the corresponding enum to be logged.
1433      *
1434      * @param sensorType See {@link FaceSensorProperties}
1435      * @return The enum to be logged
1436      */
toFaceSensorType(@aceSensorProperties.SensorType int sensorType)1437     private int toFaceSensorType(@FaceSensorProperties.SensorType int sensorType) {
1438         switch (sensorType) {
1439             case FaceSensorProperties.TYPE_RGB:
1440                 return FrameworkStatsLog
1441                         .BIOMETRIC_PROPERTIES_COLLECTED__SENSOR_TYPE__SENSOR_FACE_RGB;
1442             case FaceSensorProperties.TYPE_IR:
1443                 return FrameworkStatsLog
1444                         .BIOMETRIC_PROPERTIES_COLLECTED__SENSOR_TYPE__SENSOR_FACE_IR;
1445             default:
1446                 return FrameworkStatsLog
1447                         .BIOMETRIC_PROPERTIES_COLLECTED__SENSOR_TYPE__SENSOR_UNKNOWN;
1448         }
1449     }
1450 
1451     /**
1452      * Convert a {@link SensorProperties} sensor strength to the corresponding enum to be logged.
1453      *
1454      * @param sensorStrength See {@link SensorProperties}
1455      * @return The enum to be logged
1456      */
toSensorStrength(@ensorProperties.Strength int sensorStrength)1457     private int toSensorStrength(@SensorProperties.Strength int sensorStrength) {
1458         switch (sensorStrength) {
1459             case SensorProperties.STRENGTH_CONVENIENCE:
1460                 return FrameworkStatsLog
1461                         .BIOMETRIC_PROPERTIES_COLLECTED__SENSOR_STRENGTH__STRENGTH_CONVENIENCE;
1462             case SensorProperties.STRENGTH_WEAK:
1463                 return FrameworkStatsLog
1464                         .BIOMETRIC_PROPERTIES_COLLECTED__SENSOR_STRENGTH__STRENGTH_WEAK;
1465             case SensorProperties.STRENGTH_STRONG:
1466                 return FrameworkStatsLog
1467                         .BIOMETRIC_PROPERTIES_COLLECTED__SENSOR_STRENGTH__STRENGTH_STRONG;
1468             default:
1469                 return FrameworkStatsLog
1470                         .BIOMETRIC_PROPERTIES_COLLECTED__SENSOR_STRENGTH__STRENGTH_UNKNOWN;
1471         }
1472     }
1473 
1474     /**
1475      * A helper function to log detailed biometric sensor properties to statsd.
1476      *
1477      * @param prop The biometric sensor properties to be logged
1478      * @param modality The modality of the biometric (e.g. fingerprint, face) to be logged
1479      * @param sensorType The specific type of the biometric to be logged
1480      */
logBiometricProperties(SensorProperties prop, int modality, int sensorType)1481     private void logBiometricProperties(SensorProperties prop, int modality, int sensorType) {
1482         final int sensorId = prop.getSensorId();
1483         final int sensorStrength = toSensorStrength(prop.getSensorStrength());
1484 
1485         // Log data for each component
1486         // Note: none of the component info is a device identifier since every device of a given
1487         // model and build share the same biometric system info (see b/216195167)
1488         for (ComponentInfo componentInfo : prop.getComponentInfo()) {
1489             mBiometricLogger.logStats(
1490                     sensorId,
1491                     modality,
1492                     sensorType,
1493                     sensorStrength,
1494                     componentInfo.getComponentId().trim(),
1495                     componentInfo.getHardwareVersion().trim(),
1496                     componentInfo.getFirmwareVersion().trim(),
1497                     componentInfo.getSerialNumber().trim(),
1498                     componentInfo.getSoftwareVersion().trim());
1499         }
1500     }
1501 
1502     @VisibleForTesting
collectBiometricProperties()1503     void collectBiometricProperties() {
1504         // Check the flag to determine whether biometric property verification is enabled. It's
1505         // disabled by default.
1506         if (!DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BIOMETRICS,
1507                 KEY_ENABLE_BIOMETRIC_PROPERTY_VERIFICATION, true)) {
1508             if (DEBUG) {
1509                 Slog.d(TAG, "Do not collect/verify biometric properties. Feature disabled by "
1510                         + "DeviceConfig");
1511             }
1512             return;
1513         }
1514 
1515         PackageManager pm = mContext.getPackageManager();
1516         FingerprintManager fpManager = null;
1517         FaceManager faceManager = null;
1518         if (pm != null && pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
1519             fpManager = mContext.getSystemService(FingerprintManager.class);
1520         }
1521         if (pm != null && pm.hasSystemFeature(PackageManager.FEATURE_FACE)) {
1522             faceManager = mContext.getSystemService(FaceManager.class);
1523         }
1524 
1525         if (fpManager != null) {
1526             final int fpModality = FrameworkStatsLog
1527                     .BIOMETRIC_PROPERTIES_COLLECTED__MODALITY__MODALITY_FINGERPRINT;
1528             fpManager.addAuthenticatorsRegisteredCallback(
1529                     new IFingerprintAuthenticatorsRegisteredCallback.Stub() {
1530                         @Override
1531                         public void onAllAuthenticatorsRegistered(
1532                                 List<FingerprintSensorPropertiesInternal> sensors) {
1533                             if (DEBUG) {
1534                                 Slog.d(TAG, "Retrieve fingerprint sensor properties. "
1535                                         + "sensors.size()=" + sensors.size());
1536                             }
1537                             // Log data for each fingerprint sensor
1538                             for (FingerprintSensorPropertiesInternal propInternal : sensors) {
1539                                 final FingerprintSensorProperties prop =
1540                                         FingerprintSensorProperties.from(propInternal);
1541                                 logBiometricProperties(prop,
1542                                         fpModality,
1543                                         toFingerprintSensorType(prop.getSensorType()));
1544                             }
1545                         }
1546                     });
1547         }
1548 
1549         if (faceManager != null) {
1550             final int faceModality = FrameworkStatsLog
1551                     .BIOMETRIC_PROPERTIES_COLLECTED__MODALITY__MODALITY_FACE;
1552             faceManager.addAuthenticatorsRegisteredCallback(
1553                     new IFaceAuthenticatorsRegisteredCallback.Stub() {
1554                         @Override
1555                         public void onAllAuthenticatorsRegistered(
1556                                 List<FaceSensorPropertiesInternal> sensors) {
1557                             if (DEBUG) {
1558                                 Slog.d(TAG, "Retrieve face sensor properties. sensors.size()="
1559                                         + sensors.size());
1560                             }
1561                             // Log data for each face sensor
1562                             for (FaceSensorPropertiesInternal propInternal : sensors) {
1563                                 final FaceSensorProperties prop =
1564                                         FaceSensorProperties.from(propInternal);
1565                                 logBiometricProperties(prop,
1566                                         faceModality,
1567                                         toFaceSensorType(prop.getSensorType()));
1568                             }
1569                         }
1570                     });
1571         }
1572     }
1573 
collectBootIntegrityInfo()1574     private void collectBootIntegrityInfo() {
1575         mVbmetaDigest = SystemProperties.get(SYSPROP_NAME_VBETA_DIGEST, VBMETA_DIGEST_UNAVAILABLE);
1576         Slog.d(TAG, String.format("VBMeta Digest: %s", mVbmetaDigest));
1577         FrameworkStatsLog.write(FrameworkStatsLog.VBMETA_DIGEST_REPORTED, mVbmetaDigest);
1578 
1579         IoThread.getExecutor().execute(() -> {
1580             byte[] sepolicyHash = PackageUtils.computeSha256DigestForLargeFileAsBytes(
1581                     "/sys/fs/selinux/policy", PackageUtils.createLargeFileBuffer());
1582             String sepolicyHashEncoded = null;
1583             if (sepolicyHash != null) {
1584                 sepolicyHashEncoded = HexEncoding.encodeToString(sepolicyHash, false);
1585                 Slog.d(TAG, "sepolicy hash: " + sepolicyHashEncoded);
1586             }
1587             FrameworkStatsLog.write(FrameworkStatsLog.BOOT_INTEGRITY_INFO_REPORTED,
1588                     sepolicyHashEncoded, mVbmetaDigest);
1589         });
1590     }
1591 
1592     /**
1593      * Listen for APK updates.
1594      *
1595      * There are two ways available to us to do this:
1596      * 1. Register an observer using
1597      * {@link PackageManagerInternal#getPackageList(PackageManagerInternal.PackageListObserver)}.
1598      * 2. Register broadcast receivers, listening to either {@code ACTION_PACKAGE_ADDED} or
1599      * {@code ACTION_PACKAGE_REPLACED}.
1600      *
1601      * After experimentation, we found that Option #1 does not catch updates to non-staged APEXs.
1602      * Thus, we are implementing Option #2 here. More specifically, listening to
1603      * {@link Intent#ACTION_PACKAGE_ADDED} allows us to capture all events we care about.
1604      *
1605      * We did not use {@link Intent#ACTION_PACKAGE_REPLACED} because it unfortunately does not
1606      * detect updates to non-staged APEXs. Thus, we rely on {@link Intent#EXTRA_REPLACING} to
1607      * filter out new installation from updates instead.
1608      */
registerApkAndNonStagedApexUpdateListener()1609     private void registerApkAndNonStagedApexUpdateListener() {
1610         Slog.d(TAG, "Registering APK & Non-Staged APEX updates...");
1611         IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
1612         filter.addDataScheme("package");    // this is somehow necessary
1613         mContext.registerReceiver(new PackageUpdatedReceiver(), filter);
1614     }
1615 
1616     /**
1617      * Listen for staged-APEX updates.
1618      *
1619      * This method basically covers cases that are not caught by
1620      * {@link #registerApkAndNonStagedApexUpdateListener()}, namely updates to APEXs that are staged
1621      * for the subsequent reboot.
1622      */
registerStagedApexUpdateObserver()1623     private void registerStagedApexUpdateObserver() {
1624         Slog.d(TAG, "Registering APEX updates...");
1625         IPackageManagerNative iPackageManagerNative = IPackageManagerNative.Stub.asInterface(
1626                 ServiceManager.getService("package_native"));
1627         if (iPackageManagerNative == null) {
1628             Slog.e(TAG, "IPackageManagerNative is null");
1629             return;
1630         }
1631 
1632         try {
1633             iPackageManagerNative.registerStagedApexObserver(new IStagedApexObserver.Stub() {
1634                 @Override
1635                 public void onApexStaged(ApexStagedEvent event) throws RemoteException {
1636                     Slog.d(TAG, "A new APEX has been staged for update. There are currently "
1637                             + event.stagedApexInfos.length + " APEX(s) staged for update. "
1638                             + "Scheduling measurement...");
1639                     UpdateMeasurementsJobService.scheduleBinaryMeasurements(mContext,
1640                             BinaryTransparencyService.this);
1641                 }
1642             });
1643         } catch (RemoteException e) {
1644             Slog.e(TAG, "Failed to register a StagedApexObserver.");
1645         }
1646     }
1647 
registerBicCallback()1648     private void registerBicCallback() {
1649         if(!com.android.server.flags.Flags.optionalBackgroundInstallControl()) {
1650             Slog.d(TAG, "BICS is disabled for this device, skipping registration.");
1651             return;
1652         }
1653         IBackgroundInstallControlService iBics =
1654                 IBackgroundInstallControlService.Stub.asInterface(
1655                         ServiceManager.getService(
1656                                 Context.BACKGROUND_INSTALL_CONTROL_SERVICE));
1657         if(iBics == null) {
1658             Slog.e(TAG, "Failed to register BackgroundInstallControl callback, either "
1659                 + "background install control service does not exist or disabled on this "
1660                 + "build.");
1661             return;
1662         }
1663         try {
1664             iBics.registerBackgroundInstallCallback(
1665                     new BicCallbackHandler(
1666                         new BicCallbackHandler.IBicAppInfoHelper() {
1667                             @Override
1668                             public void writeAppInfoToLog(
1669                                     IBinaryTransparencyService.AppInfo appInfo) {
1670                                 mServiceImpl.writeAppInfoToLog(appInfo);
1671                             }
1672 
1673                             @Override
1674                             public List<IBinaryTransparencyService.AppInfo> collectAppInfo(
1675                                 PackageState packageState, int mbaStatus) {
1676                                 return mServiceImpl.collectAppInfo(packageState, mbaStatus);
1677                             }
1678                         }
1679                     ));
1680         } catch (RemoteException e) {
1681             Slog.e(TAG, "Failed to register BackgroundInstallControl callback.");
1682         }
1683     }
1684 
isPackagePreloaded(String packageName)1685     private boolean isPackagePreloaded(String packageName) {
1686         PackageManager pm = mContext.getPackageManager();
1687         try {
1688             pm.getPackageInfo(packageName, PackageManager.PackageInfoFlags.of(
1689                     PackageManager.MATCH_FACTORY_ONLY));
1690         } catch (PackageManager.NameNotFoundException e) {
1691             return false;
1692         }
1693         return true;
1694     }
1695 
isPackageAnApex(String packageName)1696     private boolean isPackageAnApex(String packageName) {
1697         PackageManager pm = mContext.getPackageManager();
1698         try {
1699             PackageInfo packageInfo = pm.getPackageInfo(packageName,
1700                     PackageManager.PackageInfoFlags.of(PackageManager.MATCH_APEX));
1701             return packageInfo.isApex;
1702         } catch (PackageManager.NameNotFoundException e) {
1703             return false;
1704         }
1705     }
1706 
1707     private class PackageUpdatedReceiver extends BroadcastReceiver {
1708         @Override
onReceive(Context context, Intent intent)1709         public void onReceive(Context context, Intent intent) {
1710             if (!Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())) {
1711                 return;
1712             }
1713 
1714             Uri data = intent.getData();
1715             if (data == null) {
1716                 Slog.e(TAG, "Shouldn't happen: intent data is null!");
1717                 return;
1718             }
1719 
1720             if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
1721                 Slog.d(TAG, "Not an update. Skipping...");
1722                 return;
1723             }
1724 
1725             String packageName = data.getSchemeSpecificPart();
1726 
1727             boolean shouldMeasureMba =
1728                 !android.app.Flags.backgroundInstallControlCallbackApi()
1729                 && isPackagePreloaded(packageName);
1730 
1731             if (shouldMeasureMba || isPackageAnApex(packageName)) {
1732                 Slog.d(TAG, packageName + " was updated. Scheduling measurement...");
1733                 UpdateMeasurementsJobService.scheduleBinaryMeasurements(mContext,
1734                         BinaryTransparencyService.this);
1735             }
1736         }
1737     }
1738 
1739     /**
1740      * Register observers for APK and APEX updates. The current implementation breaks this process
1741      * into 2 cases/methods because PackageManager does not offer a unified interface to register
1742      * for all package updates in a universal and comprehensive manner.
1743      * Thus, the observers will be invoked when either
1744      * i) APK or non-staged APEX update; or
1745      * ii) APEX staging happens.
1746      * This will then be used as signals to schedule measurement for the relevant binaries.
1747      */
registerAllPackageUpdateObservers()1748     private void registerAllPackageUpdateObservers() {
1749         registerApkAndNonStagedApexUpdateListener();
1750         registerStagedApexUpdateObserver();
1751         if (android.app.Flags.backgroundInstallControlCallbackApi()
1752                 && CompatChanges.isChangeEnabled(LOG_MBA_INFO)) {
1753             registerBicCallback();
1754         }
1755     }
1756 
translateContentDigestAlgorithmIdToString(int algorithmId)1757     private String translateContentDigestAlgorithmIdToString(int algorithmId) {
1758         switch (algorithmId) {
1759             case ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256:
1760                 return "CHUNKED_SHA256";
1761             case ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512:
1762                 return "CHUNKED_SHA512";
1763             case ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256:
1764                 return "VERITY_CHUNKED_SHA256";
1765             case ApkSigningBlockUtils.CONTENT_DIGEST_SHA256:
1766                 return "SHA256";
1767             default:
1768                 return "UNKNOWN_ALGO_ID(" + algorithmId + ")";
1769         }
1770     }
1771 
1772     @NonNull
getCurrentInstalledApexs()1773     private List<PackageInfo> getCurrentInstalledApexs() {
1774         List<PackageInfo> results = new ArrayList<>();
1775         PackageManager pm = mContext.getPackageManager();
1776         if (pm == null) {
1777             Slog.e(TAG, "Error obtaining an instance of PackageManager.");
1778             return results;
1779         }
1780         List<PackageInfo> allPackages = pm.getInstalledPackages(
1781                 PackageManager.PackageInfoFlags.of(PackageManager.MATCH_APEX
1782                         | PackageManager.GET_SIGNING_CERTIFICATES));
1783         if (allPackages == null) {
1784             Slog.e(TAG, "Error obtaining installed packages (including APEX)");
1785             return results;
1786         }
1787 
1788         results = allPackages.stream().filter(p -> p.isApex).collect(Collectors.toList());
1789         return results;
1790     }
1791 
1792     @Nullable
getInstallSourceInfo(String packageName)1793     private InstallSourceInfo getInstallSourceInfo(String packageName) {
1794         PackageManager pm = mContext.getPackageManager();
1795         if (pm == null) {
1796             Slog.e(TAG, "Error obtaining an instance of PackageManager.");
1797             return null;
1798         }
1799         try {
1800             return pm.getInstallSourceInfo(packageName);
1801         } catch (PackageManager.NameNotFoundException e) {
1802             e.printStackTrace();
1803             return null;
1804         }
1805     }
1806 
1807     @NonNull
getOriginalApexPreinstalledLocation(String packageName)1808     private String getOriginalApexPreinstalledLocation(String packageName) {
1809         try {
1810             final String moduleName = apexPackageNameToModuleName(packageName);
1811             IApexService apexService = IApexService.Stub.asInterface(
1812                     Binder.allowBlocking(ServiceManager.waitForService("apexservice")));
1813             for (ApexInfo info : apexService.getAllPackages()) {
1814                 if (moduleName.equals(info.moduleName)) {
1815                     return info.preinstalledModulePath;
1816                 }
1817             }
1818         } catch (RemoteException e) {
1819             Slog.e(TAG, "Unable to get package list from apexservice", e);
1820         }
1821         return APEX_PRELOAD_LOCATION_ERROR;
1822     }
1823 
apexPackageNameToModuleName(String packageName)1824     private String apexPackageNameToModuleName(String packageName) {
1825         // It appears that only apexd knows the preinstalled location, and it uses module name as
1826         // the identifier instead of package name. Given the input is a package name, we need to
1827         // covert to module name.
1828         return ApexManager.getInstance().getApexModuleNameForPackageName(packageName);
1829     }
1830 
1831     /**
1832      * Wrapper method to call into IBICS to get a list of all newly installed MBAs.
1833      *
1834      * We expect IBICS to maintain an accurate list of installed MBAs, and we merely make use of
1835      * the results within this service. This means we do not further check whether the
1836      * apps in the returned slice is still installed or not, esp. considering that preloaded apps
1837      * could be updated, or post-setup installed apps *might* be deleted in real time.
1838      *
1839      * Note that we do *not* cache the results from IBICS because of the more dynamic nature of
1840      * MBAs v.s. other binaries that we measure.
1841      *
1842      * @return a list of preloaded apps + dynamically installed apps that fit the definition of MBA.
1843      */
1844     @NonNull
getNewlyInstalledMbas()1845     private List<PackageInfo> getNewlyInstalledMbas() {
1846         List<PackageInfo> result = new ArrayList<>();
1847         IBackgroundInstallControlService iBics = IBackgroundInstallControlService.Stub.asInterface(
1848                 ServiceManager.getService(Context.BACKGROUND_INSTALL_CONTROL_SERVICE));
1849         if (iBics == null) {
1850             Slog.e(TAG,
1851                     "Failed to obtain an IBinder instance of IBackgroundInstallControlService");
1852             return result;
1853         }
1854         ParceledListSlice<PackageInfo> slice;
1855         try {
1856             slice = iBics.getBackgroundInstalledPackages(
1857                     PackageManager.MATCH_ALL | PackageManager.GET_SIGNING_CERTIFICATES,
1858                     UserHandle.USER_SYSTEM);
1859         } catch (RemoteException e) {
1860             Slog.e(TAG, "Failed to get a list of MBAs.", e);
1861             return result;
1862         }
1863         return slice.getList();
1864     }
1865 
Digest(int algorithm, byte[] value)1866     private record Digest(int algorithm, byte[] value) {}
1867 }
1868