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.managedprovisioning.task;
18 
19 import static com.android.internal.util.Preconditions.checkNotNull;
20 
21 import static java.util.Objects.requireNonNull;
22 
23 import android.content.Context;
24 import android.content.pm.PackageInfo;
25 import android.content.pm.PackageManager;
26 
27 import com.android.internal.annotations.VisibleForTesting;
28 import com.android.managedprovisioning.analytics.MetricsWriterFactory;
29 import com.android.managedprovisioning.analytics.ProvisioningAnalyticsTracker;
30 import com.android.managedprovisioning.common.ManagedProvisioningSharedPreferences;
31 import com.android.managedprovisioning.common.ProvisionLogger;
32 import com.android.managedprovisioning.common.SettingsFacade;
33 import com.android.managedprovisioning.common.Utils;
34 import com.android.managedprovisioning.model.PackageDownloadInfo;
35 import com.android.managedprovisioning.model.ProvisioningParams;
36 
37 import java.io.File;
38 
39 /**
40  * Verifies the management app apk downloaded previously in {@link DownloadPackageTask}.
41  *
42  * <p>The first check verifies that a {@link android.app.admin.DeviceAdminReceiver} is present in
43  * the apk and that it corresponds to the one provided via
44  * {@link ProvisioningParams#deviceAdminComponentName}.</p>
45  *
46  * <p>The second check verifies that the package or signature checksum matches the ones given via
47  * {@link PackageDownloadInfo#packageChecksum} or {@link PackageDownloadInfo#signatureChecksum}
48  * respectively. The package checksum takes priority in case both are present.</p>
49  */
50 public class VerifyAdminPackageTask extends AbstractProvisioningTask {
51     public static final int ERROR_HASH_MISMATCH = 0;
52     public static final int ERROR_DEVICE_ADMIN_MISSING = 1;
53 
54     private final Utils mUtils;
55     private final PackageLocationProvider mDownloadLocationProvider;
56     private final PackageManager mPackageManager;
57     private final PackageDownloadInfo mPackageDownloadInfo;
58     private final ChecksumUtils mChecksumUtils;
59 
VerifyAdminPackageTask( PackageLocationProvider downloadLocationProvider, Context context, ProvisioningParams params, PackageDownloadInfo packageDownloadInfo, Callback callback)60     public VerifyAdminPackageTask(
61             PackageLocationProvider downloadLocationProvider,
62             Context context,
63             ProvisioningParams params,
64             PackageDownloadInfo packageDownloadInfo,
65             Callback callback) {
66         this(new Utils(), downloadLocationProvider, context, params, packageDownloadInfo, callback,
67                 new ProvisioningAnalyticsTracker(
68                         MetricsWriterFactory.getMetricsWriter(context, new SettingsFacade()),
69                         new ManagedProvisioningSharedPreferences(context)),
70                 new ChecksumUtils(new Utils()));
71     }
72 
73     @VisibleForTesting
VerifyAdminPackageTask( Utils utils, PackageLocationProvider downloadLocationProvider, Context context, ProvisioningParams params, PackageDownloadInfo packageDownloadInfo, Callback callback, ProvisioningAnalyticsTracker provisioningAnalyticsTracker, ChecksumUtils checksumUtils)74     VerifyAdminPackageTask(
75             Utils utils,
76             PackageLocationProvider downloadLocationProvider,
77             Context context,
78             ProvisioningParams params,
79             PackageDownloadInfo packageDownloadInfo,
80             Callback callback,
81             ProvisioningAnalyticsTracker provisioningAnalyticsTracker,
82             ChecksumUtils checksumUtils) {
83         super(context, params, callback, provisioningAnalyticsTracker);
84 
85         mUtils = checkNotNull(utils);
86         mDownloadLocationProvider = checkNotNull(downloadLocationProvider);
87         mPackageManager = mContext.getPackageManager();
88         mPackageDownloadInfo = checkNotNull(packageDownloadInfo);
89         mChecksumUtils = requireNonNull(checksumUtils);
90     }
91 
92     @Override
run(int userId)93     public void run(int userId) {
94         final File packageLocation = mDownloadLocationProvider.getPackageLocation();
95         if (packageLocation == null) {
96             ProvisionLogger.logw("VerifyPackageTask invoked, but package is null");
97             success();
98             return;
99         }
100         ProvisionLogger.logi("Verifying package from location " + packageLocation.getAbsolutePath()
101                 + " for user " + userId);
102 
103         PackageInfo packageInfo = mPackageManager.getPackageArchiveInfo(
104                 packageLocation.getAbsolutePath(),
105                 PackageManager.GET_SIGNATURES | PackageManager.GET_RECEIVERS);
106         String packageName = mProvisioningParams.inferDeviceAdminPackageName();
107         // Device admin package name can't be null
108         if (packageInfo == null || packageName == null) {
109             ProvisionLogger.loge("Device admin package info or name is null");
110             error(ERROR_DEVICE_ADMIN_MISSING);
111             return;
112         }
113 
114         if (mUtils.findDeviceAdminInPackageInfo(packageName,
115                 mProvisioningParams.deviceAdminComponentName, packageInfo) == null) {
116             error(ERROR_DEVICE_ADMIN_MISSING);
117             return;
118         }
119 
120         if (mPackageDownloadInfo.packageChecksum.length > 0) {
121             if (!mChecksumUtils.doesPackageHashMatch(
122                     packageLocation.getAbsolutePath(), mPackageDownloadInfo.packageChecksum)) {
123                 error(ERROR_HASH_MISMATCH);
124                 return;
125             }
126         } else {
127             if (!mChecksumUtils.doesASignatureHashMatch(
128                     packageInfo, mPackageDownloadInfo.signatureChecksum)) {
129                 error(ERROR_HASH_MISMATCH);
130                 return;
131             }
132         }
133 
134         success();
135     }
136 }
137