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