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 android.app.DownloadManager; 19 import android.content.ComponentName; 20 import android.content.Context; 21 import android.content.pm.IPackageInstallObserver; 22 import android.content.pm.ActivityInfo; 23 import android.content.pm.PackageInfo; 24 import android.content.pm.PackageManager; 25 import android.net.Uri; 26 import android.provider.Settings.Global; 27 import android.text.TextUtils; 28 import android.Manifest.permission; 29 30 import com.android.managedprovisioning.ProvisionLogger; 31 import com.android.managedprovisioning.model.ProvisioningParams; 32 33 import java.io.File; 34 import java.util.HashSet; 35 import java.util.Set; 36 37 /** 38 * Installs all packages that were added. Can install a downloaded apk, or install an existing 39 * package which is already installed for a different user. 40 * <p> 41 * Before installing from a downloaded file, each file is checked to ensure it contains the correct 42 * package and admin receiver. 43 * </p> 44 */ 45 public class InstallPackageTask { 46 public static final int ERROR_PACKAGE_INVALID = 0; 47 public static final int ERROR_INSTALLATION_FAILED = 1; 48 public static final int ERROR_PACKAGE_NAME_INVALID = 2; 49 50 private final Context mContext; 51 private final Callback mCallback; 52 53 private PackageManager mPm; 54 private int mPackageVerifierEnable; 55 private Set<InstallInfo> mPackagesToInstall; 56 57 /** 58 * Create an InstallPackageTask. When run, this will attempt to install the device admin 59 * packages if it is non-null. 60 * 61 * {@see #run(String, String)} for more detail on package installation. 62 */ InstallPackageTask(Context context, Callback callback)63 public InstallPackageTask (Context context, Callback callback) { 64 mCallback = callback; 65 mContext = context; 66 mPackagesToInstall = new HashSet<InstallInfo>(); 67 mPm = mContext.getPackageManager(); 68 } 69 70 /** 71 * Should be called before {@link #run}. 72 */ addInstallIfNecessary(String packageName, String packageLocation)73 public void addInstallIfNecessary(String packageName, String packageLocation) { 74 if (!TextUtils.isEmpty(packageName)) { 75 mPackagesToInstall.add(new InstallInfo(packageName, packageLocation)); 76 } 77 } 78 79 /** 80 * Install all packages given by {@link #addPackageToInstall}. Each package will be installed 81 * from the given location if one is provided. If a null or empty location is provided, and the 82 * package is installed for a different user, it will be enabled for the calling user. If the 83 * package location is not provided and the package is not installed for any other users, this 84 * task will produce an error. 85 * 86 * Errors will be indicated if a downloaded package is invalid, or installation fails. 87 */ run()88 public void run() { 89 if (mPackagesToInstall.size() == 0) { 90 ProvisionLogger.loge("No downloaded packages to install"); 91 mCallback.onSuccess(); 92 return; 93 } 94 ProvisionLogger.logi("Installing package(s)"); 95 96 for (InstallInfo info : mPackagesToInstall) { 97 if (TextUtils.isEmpty(info.location)) { 98 installExistingPackage(info); 99 100 } else if (packageContentIsCorrect(info.packageName, info.location)) { 101 // Temporarily turn off package verification. 102 mPackageVerifierEnable = Global.getInt(mContext.getContentResolver(), 103 Global.PACKAGE_VERIFIER_ENABLE, 1); 104 Global.putInt(mContext.getContentResolver(), Global.PACKAGE_VERIFIER_ENABLE, 0); 105 106 // Allow for replacing an existing package. 107 // Needed in case this task is performed multiple times. 108 mPm.installPackage(Uri.parse("file://" + info.location), 109 new PackageInstallObserver(info), 110 /* flags */ PackageManager.INSTALL_REPLACE_EXISTING, 111 mContext.getPackageName()); 112 } else { 113 // Error should have been reported in packageContentIsCorrect(). 114 return; 115 } 116 } 117 } 118 packageContentIsCorrect(String packageName, String packageLocation)119 private boolean packageContentIsCorrect(String packageName, String packageLocation) { 120 PackageInfo pi = mPm.getPackageArchiveInfo(packageLocation, PackageManager.GET_RECEIVERS); 121 if (pi == null) { 122 ProvisionLogger.loge("Package could not be parsed successfully."); 123 mCallback.onError(ERROR_PACKAGE_INVALID); 124 return false; 125 } 126 if (!pi.packageName.equals(packageName)) { 127 ProvisionLogger.loge("Package name in apk (" + pi.packageName 128 + ") does not match package name specified by programmer (" 129 + packageName + ")."); 130 mCallback.onError(ERROR_PACKAGE_INVALID); 131 return false; 132 } 133 if (pi.receivers != null) { 134 for (ActivityInfo ai : pi.receivers) { 135 if (!TextUtils.isEmpty(ai.permission) && 136 ai.permission.equals(android.Manifest.permission.BIND_DEVICE_ADMIN)) { 137 return true; 138 } 139 } 140 } 141 ProvisionLogger.loge("Installed package has no admin receiver."); 142 mCallback.onError(ERROR_PACKAGE_INVALID); 143 return false; 144 } 145 146 private class PackageInstallObserver extends IPackageInstallObserver.Stub { 147 private final InstallInfo mInstallInfo; 148 PackageInstallObserver(InstallInfo installInfo)149 public PackageInstallObserver(InstallInfo installInfo) { 150 mInstallInfo = installInfo; 151 } 152 153 @Override packageInstalled(String packageName, int returnCode)154 public void packageInstalled(String packageName, int returnCode) { 155 if (packageName != null && !packageName.equals(mInstallInfo.packageName)) { 156 ProvisionLogger.loge("Package doesn't have expected package name."); 157 mCallback.onError(ERROR_PACKAGE_INVALID); 158 return; 159 } 160 if (returnCode == PackageManager.INSTALL_SUCCEEDED) { 161 mInstallInfo.doneInstalling = true; 162 ProvisionLogger.logd( 163 "Package " + mInstallInfo.packageName + " is succesfully installed."); 164 checkSuccess(); 165 } else if (returnCode == PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE) { 166 mInstallInfo.doneInstalling = true; 167 ProvisionLogger.logd("Current version of " + mInstallInfo.packageName 168 + " higher than the version to be installed. It was not reinstalled."); 169 checkSuccess(); 170 } else { 171 ProvisionLogger.logd( 172 "Installing package " + mInstallInfo.packageName + " failed."); 173 ProvisionLogger.logd( 174 "Errorcode returned by IPackageInstallObserver = " + returnCode); 175 mCallback.onError(ERROR_INSTALLATION_FAILED); 176 } 177 // remove the file containing the apk in order not to use too much space. 178 new File(mInstallInfo.location).delete(); 179 } 180 } 181 182 /** 183 * Calls the success callback once all of the packages that needed to be installed are 184 * successfully installed. 185 */ checkSuccess()186 private void checkSuccess() { 187 for (InstallInfo info : mPackagesToInstall) { 188 if (!info.doneInstalling) { 189 return; 190 } 191 } 192 // Set package verification flag to its original value. 193 Global.putInt(mContext.getContentResolver(), Global.PACKAGE_VERIFIER_ENABLE, 194 mPackageVerifierEnable); 195 mCallback.onSuccess(); 196 } 197 198 /** 199 * Attempt to install this package from an existing package installed under a different user. 200 * If this package is already installed for this user, this is a no-op. If it is not installed 201 * for another user, this will produce an error. 202 * @param info The package to install 203 */ installExistingPackage(InstallInfo info)204 private void installExistingPackage(InstallInfo info) { 205 try { 206 ProvisionLogger.logi("Installing existing package " + info.packageName); 207 mPm.installExistingPackage(info.packageName); 208 info.doneInstalling = true; 209 } catch (PackageManager.NameNotFoundException e) { 210 mCallback.onError(ERROR_PACKAGE_INVALID); 211 return; 212 } 213 checkSuccess(); 214 } 215 216 public abstract static class Callback { onSuccess()217 public abstract void onSuccess(); onError(int errorCode)218 public abstract void onError(int errorCode); 219 } 220 221 private static class InstallInfo { 222 public String packageName; 223 public String location; 224 public boolean doneInstalling; 225 InstallInfo(String packageName, String location)226 public InstallInfo(String packageName, String location) { 227 this.packageName = packageName; 228 this.location = location; 229 } 230 } 231 } 232