• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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