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.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 initializer 59 * and device admin packages if they are 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 for (ActivityInfo ai : pi.receivers) { 134 if (!TextUtils.isEmpty(ai.permission) && 135 ai.permission.equals(android.Manifest.permission.BIND_DEVICE_ADMIN)) { 136 return true; 137 } 138 } 139 ProvisionLogger.loge("Installed package has no admin receiver."); 140 mCallback.onError(ERROR_PACKAGE_INVALID); 141 return false; 142 } 143 144 private class PackageInstallObserver extends IPackageInstallObserver.Stub { 145 private final InstallInfo mInstallInfo; 146 PackageInstallObserver(InstallInfo installInfo)147 public PackageInstallObserver(InstallInfo installInfo) { 148 mInstallInfo = installInfo; 149 } 150 151 @Override packageInstalled(String packageName, int returnCode)152 public void packageInstalled(String packageName, int returnCode) { 153 if (packageName != null && !packageName.equals(mInstallInfo.packageName)) { 154 ProvisionLogger.loge("Package doesn't have expected package name."); 155 mCallback.onError(ERROR_PACKAGE_INVALID); 156 return; 157 } 158 if (returnCode == PackageManager.INSTALL_SUCCEEDED) { 159 mInstallInfo.doneInstalling = true; 160 ProvisionLogger.logd( 161 "Package " + mInstallInfo.packageName + " is succesfully installed."); 162 checkSuccess(); 163 } else if (returnCode == PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE) { 164 mInstallInfo.doneInstalling = true; 165 ProvisionLogger.logd("Current version of " + mInstallInfo.packageName 166 + " higher than the version to be installed. It was not reinstalled."); 167 checkSuccess(); 168 } else { 169 ProvisionLogger.logd( 170 "Installing package " + mInstallInfo.packageName + " failed."); 171 ProvisionLogger.logd( 172 "Errorcode returned by IPackageInstallObserver = " + returnCode); 173 mCallback.onError(ERROR_INSTALLATION_FAILED); 174 } 175 } 176 } 177 178 /** 179 * Calls the success callback once all of the packages that needed to be installed are 180 * successfully installed. 181 */ checkSuccess()182 private void checkSuccess() { 183 for (InstallInfo info : mPackagesToInstall) { 184 if (!info.doneInstalling) { 185 return; 186 } 187 } 188 // Set package verification flag to its original value. 189 Global.putInt(mContext.getContentResolver(), Global.PACKAGE_VERIFIER_ENABLE, 190 mPackageVerifierEnable); 191 mCallback.onSuccess(); 192 } 193 194 /** 195 * Attempt to install this package from an existing package installed under a different user. 196 * If this package is already installed for this user, this is a no-op. If it is not installed 197 * for another user, this will produce an error. 198 * @param info The package to install 199 */ installExistingPackage(InstallInfo info)200 private void installExistingPackage(InstallInfo info) { 201 try { 202 ProvisionLogger.logi("Installing existing package " + info.packageName); 203 mPm.installExistingPackage(info.packageName); 204 info.doneInstalling = true; 205 } catch (PackageManager.NameNotFoundException e) { 206 mCallback.onError(ERROR_PACKAGE_INVALID); 207 return; 208 } 209 checkSuccess(); 210 } 211 212 public abstract static class Callback { onSuccess()213 public abstract void onSuccess(); onError(int errorCode)214 public abstract void onError(int errorCode); 215 } 216 217 private static class InstallInfo { 218 public String packageName; 219 public String location; 220 public boolean doneInstalling; 221 InstallInfo(String packageName, String location)222 public InstallInfo(String packageName, String location) { 223 this.packageName = packageName; 224 this.location = location; 225 } 226 } 227 } 228