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 19 .PROVISIONING_INSTALL_PACKAGE_TASK_MS; 20 import static com.android.internal.util.Preconditions.checkNotNull; 21 22 import android.annotation.NonNull; 23 import android.app.PendingIntent; 24 import android.app.admin.DevicePolicyManager; 25 import android.content.BroadcastReceiver; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.IntentFilter; 29 import android.content.pm.PackageInstaller; 30 import android.content.pm.PackageManager; 31 import android.text.TextUtils; 32 33 import com.android.internal.annotations.VisibleForTesting; 34 import com.android.managedprovisioning.R; 35 import com.android.managedprovisioning.common.ProvisionLogger; 36 import com.android.managedprovisioning.common.SettingsFacade; 37 import com.android.managedprovisioning.model.ProvisioningParams; 38 39 import java.io.File; 40 import java.io.FileInputStream; 41 import java.io.IOException; 42 import java.io.InputStream; 43 import java.io.OutputStream; 44 45 /** 46 * Installs the management app apk from a download location provided by 47 * {@link DownloadPackageTask#getDownloadedPackageLocation()}. 48 */ 49 public class InstallPackageTask extends AbstractProvisioningTask { 50 private static final String ACTION_INSTALL_DONE = InstallPackageTask.class.getName() + ".DONE."; 51 52 public static final int ERROR_PACKAGE_INVALID = 0; 53 public static final int ERROR_INSTALLATION_FAILED = 1; 54 55 private final SettingsFacade mSettingsFacade; 56 private final DownloadPackageTask mDownloadPackageTask; 57 58 private final PackageManager mPm; 59 private final DevicePolicyManager mDpm; 60 private boolean mInitialPackageVerifierEnabled; 61 62 /** 63 * Create an InstallPackageTask. When run, this will attempt to install the device admin package 64 * if it is non-null. 65 * 66 * {@see #run(String, String)} for more detail on package installation. 67 */ 68 public InstallPackageTask( 69 DownloadPackageTask downloadPackageTask, 70 Context context, 71 ProvisioningParams params, 72 Callback callback) { 73 this(new SettingsFacade(), downloadPackageTask, context, params, callback); 74 } 75 76 @VisibleForTesting 77 InstallPackageTask( 78 SettingsFacade settingsFacade, 79 DownloadPackageTask downloadPackageTask, 80 Context context, 81 ProvisioningParams params, 82 Callback callback) { 83 super(context, params, callback); 84 85 mPm = context.getPackageManager(); 86 mDpm = context.getSystemService(DevicePolicyManager.class); 87 mSettingsFacade = checkNotNull(settingsFacade); 88 mDownloadPackageTask = checkNotNull(downloadPackageTask); 89 } 90 91 @Override 92 public int getStatusMsgId() { 93 return R.string.progress_install; 94 } 95 96 private static void copyStream(@NonNull InputStream in, @NonNull OutputStream out) 97 throws IOException { 98 byte[] buffer = new byte[16 * 1024]; 99 int numRead; 100 while ((numRead = in.read(buffer)) != -1) { 101 out.write(buffer, 0, numRead); 102 } 103 } 104 105 /** 106 * Installs a package. The package will be installed from the given location if one is provided. 107 * If a null or empty location is provided, and the package is installed for a different user, 108 * it will be enabled for the calling user. If the package location is not provided and the 109 * package is not installed for any other users, this task will produce an error. 110 * 111 * Errors will be indicated if a downloaded package is invalid, or installation fails. 112 */ 113 @Override 114 public void run(int userId) { 115 startTaskTimer(); 116 String packageLocation = mDownloadPackageTask.getDownloadedPackageLocation(); 117 String packageName = mProvisioningParams.inferDeviceAdminPackageName(); 118 119 ProvisionLogger.logi("Installing package " + packageName); 120 mInitialPackageVerifierEnabled = mSettingsFacade.isPackageVerifierEnabled(mContext); 121 if (TextUtils.isEmpty(packageLocation)) { 122 // Do not log time if not installing any package, as that isn't useful. 123 success(); 124 return; 125 } 126 127 // Temporarily turn off package verification. 128 mSettingsFacade.setPackageVerifierEnabled(mContext, false); 129 130 int installFlags = PackageManager.INSTALL_REPLACE_EXISTING; 131 // Current device owner (if exists) must be test-only, so it is fine to replace it with a 132 // test-only package of same package name. No need to further verify signature as 133 // installation will fail if signatures don't match. 134 if (mDpm.isDeviceOwnerApp(packageName)) { 135 installFlags |= PackageManager.INSTALL_ALLOW_TEST; 136 } 137 138 PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( 139 PackageInstaller.SessionParams.MODE_FULL_INSTALL); 140 params.installFlags = installFlags; 141 142 File source = new File(packageLocation); 143 PackageInstaller pi = mPm.getPackageInstaller(); 144 try { 145 int sessionId = pi.createSession(params); 146 try (PackageInstaller.Session session = pi.openSession(sessionId)) { 147 try (FileInputStream in = new FileInputStream(source); 148 OutputStream out = session.openWrite(source.getName(), 0, -1)) { 149 copyStream(in, out); 150 } catch (IOException e) { 151 session.abandon(); 152 throw e; 153 } 154 155 String action = ACTION_INSTALL_DONE + sessionId; 156 mContext.registerReceiver(new PackageInstallReceiver(packageName), 157 new IntentFilter(action)); 158 159 PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, sessionId, 160 new Intent(action), 161 PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT); 162 session.commit(pendingIntent.getIntentSender()); 163 } 164 } catch (IOException e) { 165 mSettingsFacade.setPackageVerifierEnabled(mContext, mInitialPackageVerifierEnabled); 166 167 ProvisionLogger.loge("Installing package " + packageName + " failed.", e); 168 error(ERROR_INSTALLATION_FAILED); 169 } finally { 170 source.delete(); 171 } 172 } 173 174 @Override 175 protected int getMetricsCategory() { 176 return PROVISIONING_INSTALL_PACKAGE_TASK_MS; 177 } 178 179 private class PackageInstallReceiver extends BroadcastReceiver { 180 private final String mPackageName; 181 182 public PackageInstallReceiver(String packageName) { 183 mPackageName = packageName; 184 } 185 186 @Override 187 public void onReceive(Context context, Intent intent) { 188 mSettingsFacade.setPackageVerifierEnabled(mContext, mInitialPackageVerifierEnabled); 189 190 // Should not happen as we use a one shot pending intent specifically for this receiver 191 if (intent.getAction() == null || !intent.getAction().startsWith(ACTION_INSTALL_DONE)) { 192 ProvisionLogger.logw("Incorrect action"); 193 194 error(ERROR_INSTALLATION_FAILED); 195 return; 196 } 197 198 // Should not happen as we use a one shot pending intent specifically for this receiver 199 if (!intent.getStringExtra(PackageInstaller.EXTRA_PACKAGE_NAME).equals(mPackageName)) { 200 ProvisionLogger.loge("Package doesn't have expected package name."); 201 error(ERROR_PACKAGE_INVALID); 202 return; 203 } 204 205 int status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, 0); 206 String statusMessage = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE); 207 int legacyStatus = intent.getIntExtra(PackageInstaller.EXTRA_LEGACY_STATUS, 0); 208 209 mContext.unregisterReceiver(this); 210 ProvisionLogger.logi(status + " " + legacyStatus + " " + statusMessage); 211 212 if (status == PackageInstaller.STATUS_SUCCESS) { 213 ProvisionLogger.logd("Package " + mPackageName + " is succesfully installed."); 214 stopTaskTimer(); 215 success(); 216 } else if (legacyStatus == PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE) { 217 ProvisionLogger.logd("Current version of " + mPackageName 218 + " higher than the version to be installed. It was not reinstalled."); 219 // If the package is already at a higher version: success. 220 // Do not log time if package is already at a higher version, as that isn't useful. 221 success(); 222 } else { 223 ProvisionLogger.logd("Installing package " + mPackageName + " failed."); 224 ProvisionLogger.logd("Status message returned = " + statusMessage); 225 error(ERROR_INSTALLATION_FAILED); 226 } 227 } 228 } 229 } 230