1 /*
2  * Copyright 2014, 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 package com.android.managedprovisioning.task;
17 
18 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_INSTALL_PACKAGE_TASK_MS;
19 import static com.android.internal.util.Preconditions.checkNotNull;
20 
21 import android.content.Context;
22 import android.content.pm.IPackageInstallObserver;
23 import android.content.pm.PackageManager;
24 import android.net.Uri;
25 import android.text.TextUtils;
26 
27 import com.android.internal.annotations.VisibleForTesting;
28 import com.android.managedprovisioning.common.ProvisionLogger;
29 import com.android.managedprovisioning.R;
30 import com.android.managedprovisioning.common.SettingsFacade;
31 import com.android.managedprovisioning.model.ProvisioningParams;
32 
33 import java.io.File;
34 
35 /**
36  * Installs the management app apk from a download location provided by
37  * {@link DownloadPackageTask#getDownloadedPackageLocation()}.
38  */
39 public class InstallPackageTask extends AbstractProvisioningTask {
40     public static final int ERROR_PACKAGE_INVALID = 0;
41     public static final int ERROR_INSTALLATION_FAILED = 1;
42 
43     private final SettingsFacade mSettingsFacade;
44     private final DownloadPackageTask mDownloadPackageTask;
45 
46     private PackageManager mPm;
47     private boolean mInitialPackageVerifierEnabled;
48 
49     /**
50      * Create an InstallPackageTask. When run, this will attempt to install the device admin package
51      * if it is non-null.
52      *
53      * {@see #run(String, String)} for more detail on package installation.
54      */
InstallPackageTask( DownloadPackageTask downloadPackageTask, Context context, ProvisioningParams params, Callback callback)55     public InstallPackageTask(
56             DownloadPackageTask downloadPackageTask,
57             Context context,
58             ProvisioningParams params,
59             Callback callback) {
60         this(new SettingsFacade(), downloadPackageTask, context, params, callback);
61     }
62 
63     @VisibleForTesting
InstallPackageTask( SettingsFacade settingsFacade, DownloadPackageTask downloadPackageTask, Context context, ProvisioningParams params, Callback callback)64     InstallPackageTask(
65             SettingsFacade settingsFacade,
66             DownloadPackageTask downloadPackageTask,
67             Context context,
68             ProvisioningParams params,
69             Callback callback) {
70         super(context, params, callback);
71 
72         mPm = context.getPackageManager();
73         mSettingsFacade = checkNotNull(settingsFacade);
74         mDownloadPackageTask = checkNotNull(downloadPackageTask);
75     }
76 
77     @Override
getStatusMsgId()78     public int getStatusMsgId() {
79         return R.string.progress_install;
80     }
81 
82     /**
83      * Installs a package. The package will be installed from the given location if one is provided.
84      * If a null or empty location is provided, and the package is installed for a different user,
85      * it will be enabled for the calling user. If the package location is not provided and the
86      * package is not installed for any other users, this task will produce an error.
87      *
88      * Errors will be indicated if a downloaded package is invalid, or installation fails.
89      */
90     @Override
run(int userId)91     public void run(int userId) {
92         startTaskTimer();
93         String packageLocation = mDownloadPackageTask.getDownloadedPackageLocation();
94         String packageName = mProvisioningParams.inferDeviceAdminPackageName();
95 
96         ProvisionLogger.logi("Installing package");
97         mInitialPackageVerifierEnabled = mSettingsFacade.isPackageVerifierEnabled(mContext);
98         if (TextUtils.isEmpty(packageLocation)) {
99             // Do not log time if not installing any package, as that isn't useful.
100             success();
101             return;
102         }
103 
104         // Temporarily turn off package verification.
105         mSettingsFacade.setPackageVerifierEnabled(mContext, false);
106 
107         // Allow for replacing an existing package.
108         // Needed in case this task is performed multiple times.
109         mPm.installPackage(Uri.parse("file://" + packageLocation),
110                 new PackageInstallObserver(packageName, packageLocation),
111                 /* flags */ PackageManager.INSTALL_REPLACE_EXISTING,
112                 mContext.getPackageName());
113     }
114 
115     @Override
getMetricsCategory()116     protected int getMetricsCategory() {
117         return PROVISIONING_INSTALL_PACKAGE_TASK_MS;
118     }
119 
120     private class PackageInstallObserver extends IPackageInstallObserver.Stub {
121         private final String mPackageName;
122         private final String mPackageLocation;
123 
PackageInstallObserver(String packageName, String packageLocation)124         public PackageInstallObserver(String packageName, String packageLocation) {
125             mPackageName = packageName;
126             mPackageLocation = packageLocation;
127         }
128 
129         @Override
packageInstalled(String packageName, int returnCode)130         public void packageInstalled(String packageName, int returnCode) {
131             mSettingsFacade.setPackageVerifierEnabled(mContext, mInitialPackageVerifierEnabled);
132             if (packageName != null && !packageName.equals(mPackageName))  {
133                 ProvisionLogger.loge("Package doesn't have expected package name.");
134                 error(ERROR_PACKAGE_INVALID);
135                 return;
136             }
137             if (returnCode == PackageManager.INSTALL_SUCCEEDED) {
138                 ProvisionLogger.logd("Package " + mPackageName + " is succesfully installed.");
139                 stopTaskTimer();
140                 success();
141             } else if (returnCode == PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE) {
142                 ProvisionLogger.logd("Current version of " + mPackageName
143                         + " higher than the version to be installed. It was not reinstalled.");
144                 // If the package is already at a higher version: success.
145                 // Do not log time if package is already at a higher version, as that isn't useful.
146                 success();
147             } else {
148                 ProvisionLogger.logd(
149                         "Installing package " + mPackageName + " failed.");
150                 ProvisionLogger.logd(
151                         "Errorcode returned by IPackageInstallObserver = " + returnCode);
152                 error(ERROR_INSTALLATION_FAILED);
153             }
154             // remove the file containing the apk in order not to use too much space.
155             new File(mPackageLocation).delete();
156         }
157     }
158 }
159