blob: 871ed5c7dbad69950a2135b07d4ac331f5e8f182 [file] [log] [blame]
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -07001/*
2 * Copyright (C) 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
17package com.android.server.pm;
18
Jeff Sharkeyf0600952014-08-07 17:31:53 -070019import static android.content.pm.PackageManager.INSTALL_FAILED_ABORTED;
Jeff Sharkey742e7902014-08-16 19:09:13 -070020import static android.content.pm.PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
Jeff Sharkey941a8ba2014-08-20 16:26:32 -070021import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070022import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
23import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
Jeff Sharkeyec55ef02014-07-08 11:28:00 -070024import static android.system.OsConstants.O_CREAT;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070025import static android.system.OsConstants.O_RDONLY;
Jeff Sharkeyec55ef02014-07-08 11:28:00 -070026import static android.system.OsConstants.O_WRONLY;
Jeff Sharkeyd5d5e922017-02-21 10:51:23 -070027
Philip P. Moltmann7460c592017-08-08 20:07:11 +000028import static com.android.internal.util.XmlUtils.readBitmapAttribute;
29import static com.android.internal.util.XmlUtils.readBooleanAttribute;
30import static com.android.internal.util.XmlUtils.readIntAttribute;
31import static com.android.internal.util.XmlUtils.readLongAttribute;
32import static com.android.internal.util.XmlUtils.readStringAttribute;
33import static com.android.internal.util.XmlUtils.readUriAttribute;
34import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
35import static com.android.internal.util.XmlUtils.writeIntAttribute;
36import static com.android.internal.util.XmlUtils.writeLongAttribute;
37import static com.android.internal.util.XmlUtils.writeStringAttribute;
38import static com.android.internal.util.XmlUtils.writeUriAttribute;
Jeff Sharkey77d218e2014-09-06 12:20:37 -070039import static com.android.server.pm.PackageInstallerService.prepareExternalStageCid;
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -070040import static com.android.server.pm.PackageInstallerService.prepareStageDir;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070041
Philip P. Moltmann7460c592017-08-08 20:07:11 +000042import android.Manifest;
43import android.annotation.NonNull;
Todd Kennedy544b3832017-08-22 10:48:18 -070044import android.annotation.Nullable;
Benjamin Franz39fb7fd2015-02-18 16:11:18 +000045import android.app.admin.DevicePolicyManager;
Jeff Sharkeya0907432014-08-15 10:23:11 -070046import android.content.Context;
Jeff Sharkeyfbd0e9f2014-08-06 16:34:34 -070047import android.content.Intent;
Jeff Sharkeya0907432014-08-15 10:23:11 -070048import android.content.IntentSender;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070049import android.content.pm.ApplicationInfo;
50import android.content.pm.IPackageInstallObserver2;
51import android.content.pm.IPackageInstallerSession;
Todd Kennedyeb9b0532016-03-08 10:10:54 -080052import android.content.pm.PackageInfo;
Jeff Sharkey7328a1b2014-08-07 14:01:43 -070053import android.content.pm.PackageInstaller;
Jeff Sharkeya0907432014-08-15 10:23:11 -070054import android.content.pm.PackageInstaller.SessionInfo;
55import android.content.pm.PackageInstaller.SessionParams;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070056import android.content.pm.PackageManager;
57import android.content.pm.PackageParser;
Jeff Sharkeyc4858a22014-06-16 10:51:20 -070058import android.content.pm.PackageParser.ApkLite;
Jeff Sharkey941a8ba2014-08-20 16:26:32 -070059import android.content.pm.PackageParser.PackageLite;
Jeff Sharkey275e0852014-06-17 18:18:49 -070060import android.content.pm.PackageParser.PackageParserException;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070061import android.content.pm.Signature;
Philip P. Moltmann7460c592017-08-08 20:07:11 +000062import android.graphics.Bitmap;
63import android.graphics.BitmapFactory;
Todd Kennedyc25fbde2016-08-31 15:54:48 -070064import android.os.Binder;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070065import android.os.Bundle;
Jeff Sharkey78cc3402014-05-22 10:52:49 -070066import android.os.FileBridge;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070067import android.os.FileUtils;
68import android.os.Handler;
69import android.os.Looper;
70import android.os.Message;
71import android.os.ParcelFileDescriptor;
Philip P. Moltmann7460c592017-08-08 20:07:11 +000072import android.os.ParcelableException;
Jeff Sharkey941a8ba2014-08-20 16:26:32 -070073import android.os.Process;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070074import android.os.RemoteException;
Jeff Sharkey02d4e342017-03-10 21:53:48 -070075import android.os.RevocableFileDescriptor;
Jeff Sharkey57dcf5b2014-06-18 17:46:05 -070076import android.os.UserHandle;
Jeff Sharkeyd5d5e922017-02-21 10:51:23 -070077import android.os.storage.StorageManager;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070078import android.system.ErrnoException;
Jeff Sharkeyec55ef02014-07-08 11:28:00 -070079import android.system.Os;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070080import android.system.OsConstants;
81import android.system.StructStat;
Todd Kennedyeb9b0532016-03-08 10:10:54 -080082import android.text.TextUtils;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070083import android.util.ArraySet;
Jeff Sharkeya1031142014-07-12 18:09:46 -070084import android.util.ExceptionUtils;
85import android.util.MathUtils;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070086import android.util.Slog;
87
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070088import com.android.internal.annotations.GuardedBy;
Jeff Sharkey941a8ba2014-08-20 16:26:32 -070089import com.android.internal.content.NativeLibraryHelper;
Jeff Sharkey742e7902014-08-16 19:09:13 -070090import com.android.internal.content.PackageHelper;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070091import com.android.internal.util.ArrayUtils;
Jeff Sharkeya1031142014-07-12 18:09:46 -070092import com.android.internal.util.IndentingPrintWriter;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070093import com.android.internal.util.Preconditions;
Jeff Sharkey740f5232016-12-09 14:31:26 -070094import com.android.server.pm.Installer.InstallerException;
Jeff Sharkeya0907432014-08-15 10:23:11 -070095import com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapter;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070096
Jeff Sharkeyd5d5e922017-02-21 10:51:23 -070097import libcore.io.IoUtils;
98import libcore.io.Libcore;
99
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000100import org.xmlpull.v1.XmlPullParser;
101import org.xmlpull.v1.XmlPullParserException;
102import org.xmlpull.v1.XmlSerializer;
103
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700104import java.io.File;
105import java.io.FileDescriptor;
Todd Kennedyeb9b0532016-03-08 10:10:54 -0800106import java.io.FileFilter;
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000107import java.io.FileOutputStream;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700108import java.io.IOException;
Todd Kennedyd9d438a2016-04-06 14:08:14 -0700109import java.security.cert.Certificate;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700110import java.util.ArrayList;
Narayan Kamathcd1fc142015-05-11 13:35:59 +0100111import java.util.Arrays;
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700112import java.util.List;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700113import java.util.concurrent.atomic.AtomicInteger;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700114
115public class PackageInstallerSession extends IPackageInstallerSession.Stub {
116 private static final String TAG = "PackageInstaller";
Jeff Sharkey9a445772014-07-16 11:32:08 -0700117 private static final boolean LOGD = true;
Todd Kennedyeb9b0532016-03-08 10:10:54 -0800118 private static final String REMOVE_SPLIT_MARKER_EXTENSION = ".removed";
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700119
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700120 private static final int MSG_COMMIT = 0;
121
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000122 /** XML constants used for persisting a session */
123 static final String TAG_SESSION = "session";
124 private static final String TAG_GRANTED_RUNTIME_PERMISSION = "granted-runtime-permission";
125 private static final String ATTR_SESSION_ID = "sessionId";
126 private static final String ATTR_USER_ID = "userId";
127 private static final String ATTR_INSTALLER_PACKAGE_NAME = "installerPackageName";
128 private static final String ATTR_INSTALLER_UID = "installerUid";
129 private static final String ATTR_CREATED_MILLIS = "createdMillis";
130 private static final String ATTR_SESSION_STAGE_DIR = "sessionStageDir";
131 private static final String ATTR_SESSION_STAGE_CID = "sessionStageCid";
132 private static final String ATTR_PREPARED = "prepared";
133 private static final String ATTR_SEALED = "sealed";
134 private static final String ATTR_MODE = "mode";
135 private static final String ATTR_INSTALL_FLAGS = "installFlags";
136 private static final String ATTR_INSTALL_LOCATION = "installLocation";
137 private static final String ATTR_SIZE_BYTES = "sizeBytes";
138 private static final String ATTR_APP_PACKAGE_NAME = "appPackageName";
139 @Deprecated
140 private static final String ATTR_APP_ICON = "appIcon";
141 private static final String ATTR_APP_LABEL = "appLabel";
142 private static final String ATTR_ORIGINATING_URI = "originatingUri";
143 private static final String ATTR_ORIGINATING_UID = "originatingUid";
144 private static final String ATTR_REFERRER_URI = "referrerUri";
145 private static final String ATTR_ABI_OVERRIDE = "abiOverride";
146 private static final String ATTR_VOLUME_UUID = "volumeUuid";
147 private static final String ATTR_NAME = "name";
148 private static final String ATTR_INSTALL_REASON = "installRason";
149
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700150 // TODO: enforce INSTALL_ALLOW_TEST
151 // TODO: enforce INSTALL_ALLOW_DOWNGRADE
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700152
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700153 private final PackageInstallerService.InternalCallback mCallback;
Jeff Sharkeya0907432014-08-15 10:23:11 -0700154 private final Context mContext;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700155 private final PackageManagerService mPm;
156 private final Handler mHandler;
157
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700158 final int sessionId;
159 final int userId;
Jeff Sharkeya0907432014-08-15 10:23:11 -0700160 final SessionParams params;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700161 final long createdMillis;
Todd Kennedyf29d07a2016-08-08 15:17:43 -0700162 final int defaultContainerGid;
Jeff Sharkey742e7902014-08-16 19:09:13 -0700163
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700164 /** Staging location where client data is written. */
165 final File stageDir;
166 final String stageCid;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700167
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700168 private final AtomicInteger mActiveCount = new AtomicInteger();
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700169
170 private final Object mLock = new Object();
171
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000172 /** Uid of the creator of this session. */
173 private final int mOriginalInstallerUid;
174
175 /** Package of the owner of the installer session */
176 @GuardedBy("mLock")
177 private String mInstallerPackageName;
178
179 /** Uid of the owner of the installer session */
180 @GuardedBy("mLock")
181 private int mInstallerUid;
182
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700183 @GuardedBy("mLock")
184 private float mClientProgress = 0;
185 @GuardedBy("mLock")
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700186 private float mInternalProgress = 0;
187
188 @GuardedBy("mLock")
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700189 private float mProgress = 0;
190 @GuardedBy("mLock")
191 private float mReportedProgress = -1;
192
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000193 /** State of the session. */
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700194 @GuardedBy("mLock")
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700195 private boolean mPrepared = false;
196 @GuardedBy("mLock")
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700197 private boolean mSealed = false;
198 @GuardedBy("mLock")
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000199 private boolean mCommitted = false;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700200 @GuardedBy("mLock")
Jeff Sharkey497c0522015-05-12 13:07:14 -0700201 private boolean mRelinquished = false;
202 @GuardedBy("mLock")
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700203 private boolean mDestroyed = false;
204
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000205 /** Permissions have been accepted by the user (see {@link #setPermissionsResult}) */
206 @GuardedBy("mLock")
207 private boolean mPermissionsManuallyAccepted = false;
208
209 @GuardedBy("mLock")
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700210 private int mFinalStatus;
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000211 @GuardedBy("mLock")
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700212 private String mFinalMessage;
213
Jeff Sharkey742e7902014-08-16 19:09:13 -0700214 @GuardedBy("mLock")
Jeff Sharkey02d4e342017-03-10 21:53:48 -0700215 private final ArrayList<RevocableFileDescriptor> mFds = new ArrayList<>();
216 @GuardedBy("mLock")
217 private final ArrayList<FileBridge> mBridges = new ArrayList<>();
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700218
219 @GuardedBy("mLock")
220 private IPackageInstallObserver2 mRemoteObserver;
221
222 /** Fields derived from commit parsing */
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000223 @GuardedBy("mLock")
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700224 private String mPackageName;
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000225 @GuardedBy("mLock")
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700226 private int mVersionCode;
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000227 @GuardedBy("mLock")
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700228 private Signature[] mSignatures;
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000229 @GuardedBy("mLock")
Todd Kennedyd9d438a2016-04-06 14:08:14 -0700230 private Certificate[][] mCertificates;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700231
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700232 /**
233 * Path to the validated base APK for this session, which may point at an
234 * APK inside the session (when the session defines the base), or it may
235 * point at the existing base APK (when adding splits to an existing app).
236 * <p>
237 * This is used when confirming permissions, since we can't fully stage the
238 * session inside an ASEC before confirming with user.
239 */
240 @GuardedBy("mLock")
241 private File mResolvedBaseFile;
242
243 @GuardedBy("mLock")
244 private File mResolvedStageDir;
245
246 @GuardedBy("mLock")
247 private final List<File> mResolvedStagedFiles = new ArrayList<>();
248 @GuardedBy("mLock")
249 private final List<File> mResolvedInheritedFiles = new ArrayList<>();
Narayan Kamathcd1fc142015-05-11 13:35:59 +0100250 @GuardedBy("mLock")
Narayan Kamathe845a1e2015-06-05 11:59:26 +0100251 private final List<String> mResolvedInstructionSets = new ArrayList<>();
252 @GuardedBy("mLock")
Narayan Kamathcd1fc142015-05-11 13:35:59 +0100253 private File mInheritedFilesBase;
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700254
Todd Kennedyeb9b0532016-03-08 10:10:54 -0800255 private static final FileFilter sAddedFilter = new FileFilter() {
256 @Override
257 public boolean accept(File file) {
258 // Installers can't stage directories, so it's fine to ignore
259 // entries like "lost+found".
260 if (file.isDirectory()) return false;
261 if (file.getName().endsWith(REMOVE_SPLIT_MARKER_EXTENSION)) return false;
262 return true;
263 }
264 };
265 private static final FileFilter sRemovedFilter = new FileFilter() {
266 @Override
267 public boolean accept(File file) {
268 if (file.isDirectory()) return false;
269 if (!file.getName().endsWith(REMOVE_SPLIT_MARKER_EXTENSION)) return false;
270 return true;
271 }
272 };
273
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700274 private final Handler.Callback mHandlerCallback = new Handler.Callback() {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700275 @Override
276 public boolean handleMessage(Message msg) {
277 synchronized (mLock) {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700278 try {
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000279 commitLocked();
Jeff Sharkeye0b0bef2014-07-12 15:37:47 -0700280 } catch (PackageManagerException e) {
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700281 final String completeMsg = ExceptionUtils.getCompleteMessage(e);
282 Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
Jeff Sharkeya1031142014-07-12 18:09:46 -0700283 destroyInternal();
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700284 dispatchSessionFinished(e.error, completeMsg, null);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700285 }
Philip P. Moltmann9890f8b2017-08-08 10:49:38 -0700286 }
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000287
288 return true;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700289 }
290 };
291
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000292 /**
293 * @return {@code true} iff the installing is app an device owner?
294 */
295 private boolean isInstallerDeviceOwnerLocked() {
296 DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(
297 Context.DEVICE_POLICY_SERVICE);
298
299 return (dpm != null) && dpm.isDeviceOwnerAppOnCallingUser(
300 mInstallerPackageName);
301 }
302
303 /**
304 * Checks if the permissions still need to be confirmed.
305 *
306 * <p>This is dependant on the identity of the installer, hence this cannot be cached if the
307 * installer might still {@link #transfer(String) change}.
308 *
309 * @return {@code true} iff we need to ask to confirm the permissions?
310 */
311 private boolean needToAskForPermissionsLocked() {
312 if (mPermissionsManuallyAccepted) {
313 return false;
314 }
315
316 final boolean isPermissionGranted =
317 (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES,
318 mInstallerUid) == PackageManager.PERMISSION_GRANTED);
319 final boolean isInstallerRoot = (mInstallerUid == Process.ROOT_UID);
320 final boolean forcePermissionPrompt =
321 (params.installFlags & PackageManager.INSTALL_FORCE_PERMISSION_PROMPT) != 0;
322
323 // Device owners are allowed to silently install packages, so the permission check is
324 // waived if the installer is the device owner.
325 return forcePermissionPrompt || !(isPermissionGranted || isInstallerRoot
326 || isInstallerDeviceOwnerLocked());
327 }
328
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700329 public PackageInstallerSession(PackageInstallerService.InternalCallback callback,
Jeff Sharkeya0907432014-08-15 10:23:11 -0700330 Context context, PackageManagerService pm, Looper looper, int sessionId, int userId,
Jeff Sharkeye9808042014-09-11 21:15:37 -0700331 String installerPackageName, int installerUid, SessionParams params, long createdMillis,
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700332 File stageDir, String stageCid, boolean prepared, boolean sealed) {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700333 mCallback = callback;
Jeff Sharkeya0907432014-08-15 10:23:11 -0700334 mContext = context;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700335 mPm = pm;
336 mHandler = new Handler(looper, mHandlerCallback);
337
338 this.sessionId = sessionId;
339 this.userId = userId;
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000340 mOriginalInstallerUid = installerUid;
341 mInstallerPackageName = installerPackageName;
342 mInstallerUid = installerUid;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700343 this.params = params;
344 this.createdMillis = createdMillis;
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700345 this.stageDir = stageDir;
346 this.stageCid = stageCid;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700347
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700348 if ((stageDir == null) == (stageCid == null)) {
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -0700349 throw new IllegalArgumentException(
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700350 "Exactly one of stageDir or stageCid stage must be set");
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -0700351 }
352
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700353 mPrepared = prepared;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700354 mSealed = sealed;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700355
Todd Kennedyc25fbde2016-08-31 15:54:48 -0700356 final long identity = Binder.clearCallingIdentity();
357 try {
358 final int uid = mPm.getPackageUid(PackageManagerService.DEFAULT_CONTAINER_PACKAGE,
359 PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM);
360 defaultContainerGid = UserHandle.getSharedAppGid(uid);
361 } finally {
362 Binder.restoreCallingIdentity(identity);
363 }
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700364 }
365
Jeff Sharkeya0907432014-08-15 10:23:11 -0700366 public SessionInfo generateInfo() {
Jeff Sharkeyda1247a2017-06-08 14:13:29 -0600367 return generateInfo(true);
368 }
369
370 public SessionInfo generateInfo(boolean includeIcon) {
Jeff Sharkeya0907432014-08-15 10:23:11 -0700371 final SessionInfo info = new SessionInfo();
Jeff Sharkey742e7902014-08-16 19:09:13 -0700372 synchronized (mLock) {
373 info.sessionId = sessionId;
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000374 info.installerPackageName = mInstallerPackageName;
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700375 info.resolvedBaseCodePath = (mResolvedBaseFile != null) ?
376 mResolvedBaseFile.getAbsolutePath() : null;
Jeff Sharkey742e7902014-08-16 19:09:13 -0700377 info.progress = mProgress;
378 info.sealed = mSealed;
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700379 info.active = mActiveCount.get() > 0;
Jeff Sharkeybb580672014-07-10 12:10:25 -0700380
Jeff Sharkey742e7902014-08-16 19:09:13 -0700381 info.mode = params.mode;
Sunny Goyal6d7cb232017-01-30 10:43:18 -0800382 info.installReason = params.installReason;
Jeff Sharkey742e7902014-08-16 19:09:13 -0700383 info.sizeBytes = params.sizeBytes;
384 info.appPackageName = params.appPackageName;
Jeff Sharkeyda1247a2017-06-08 14:13:29 -0600385 if (includeIcon) {
386 info.appIcon = params.appIcon;
387 }
Jeff Sharkey742e7902014-08-16 19:09:13 -0700388 info.appLabel = params.appLabel;
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000389
390 info.installLocation = params.installLocation;
391 info.originatingUri = params.originatingUri;
392 info.originatingUid = params.originatingUid;
393 info.referrerUri = params.referrerUri;
394 info.grantedRuntimePermissions = params.grantedRuntimePermissions;
395 info.installFlags = params.installFlags;
Jeff Sharkey742e7902014-08-16 19:09:13 -0700396 }
Jeff Sharkeybb580672014-07-10 12:10:25 -0700397 return info;
398 }
399
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700400 public boolean isPrepared() {
401 synchronized (mLock) {
402 return mPrepared;
403 }
404 }
405
Jeff Sharkey742e7902014-08-16 19:09:13 -0700406 public boolean isSealed() {
407 synchronized (mLock) {
408 return mSealed;
409 }
410 }
411
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000412 private void assertPreparedAndNotSealedLocked(String cookie) {
413 assertPreparedAndNotCommittedOrDestroyedLocked(cookie);
414 if (mSealed) {
415 throw new SecurityException(cookie + " not allowed after sealing");
416 }
417 }
418
419 private void assertPreparedAndNotCommittedOrDestroyedLocked(String cookie) {
420 assertPreparedAndNotDestroyedLocked(cookie);
421 if (mCommitted) {
422 throw new SecurityException(cookie + " not allowed after commit");
423 }
424 }
425
426 private void assertPreparedAndNotDestroyedLocked(String cookie) {
427 if (!mPrepared) {
428 throw new IllegalStateException(cookie + " before prepared");
429 }
430 if (mDestroyed) {
431 throw new SecurityException(cookie + " not allowed after destruction");
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700432 }
433 }
434
Jeff Sharkey742e7902014-08-16 19:09:13 -0700435 /**
436 * Resolve the actual location where staged data should be written. This
437 * might point at an ASEC mount point, which is why we delay path resolution
438 * until someone actively works with the session.
439 */
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000440 private File resolveStageDirLocked() throws IOException {
441 if (mResolvedStageDir == null) {
442 if (stageDir != null) {
443 mResolvedStageDir = stageDir;
444 } else {
445 final String path = PackageHelper.getSdDir(stageCid);
446 if (path != null) {
447 mResolvedStageDir = new File(path);
Jeff Sharkey742e7902014-08-16 19:09:13 -0700448 } else {
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000449 throw new IOException("Failed to resolve path to container " + stageCid);
Jeff Sharkey742e7902014-08-16 19:09:13 -0700450 }
451 }
Jeff Sharkey742e7902014-08-16 19:09:13 -0700452 }
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000453 return mResolvedStageDir;
Jeff Sharkey742e7902014-08-16 19:09:13 -0700454 }
455
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700456 @Override
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700457 public void setClientProgress(float progress) {
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700458 synchronized (mLock) {
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000459 assertCallerIsOwnerOrRootLocked();
460
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700461 // Always publish first staging movement
462 final boolean forcePublish = (mClientProgress == 0);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700463 mClientProgress = progress;
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700464 computeProgressLocked(forcePublish);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700465 }
Jeff Sharkeya1031142014-07-12 18:09:46 -0700466 }
467
468 @Override
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700469 public void addClientProgress(float progress) {
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700470 synchronized (mLock) {
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000471 assertCallerIsOwnerOrRootLocked();
472
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700473 setClientProgress(mClientProgress + progress);
Jeff Sharkeyec9bad22014-09-05 09:45:20 -0700474 }
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700475 }
476
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700477 private void computeProgressLocked(boolean forcePublish) {
478 mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f)
479 + MathUtils.constrain(mInternalProgress * 0.2f, 0f, 0.2f);
480
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700481 // Only publish when meaningful change
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700482 if (forcePublish || Math.abs(mProgress - mReportedProgress) >= 0.01) {
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700483 mReportedProgress = mProgress;
484 mCallback.onSessionProgressChanged(this, mProgress);
485 }
486 }
487
488 @Override
Jeff Sharkeya0907432014-08-15 10:23:11 -0700489 public String[] getNames() {
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000490 synchronized (mLock) {
491 assertCallerIsOwnerOrRootLocked();
492 assertPreparedAndNotCommittedOrDestroyedLocked("getNames");
493
494 try {
495 return resolveStageDirLocked().list();
496 } catch (IOException e) {
497 throw ExceptionUtils.wrap(e);
498 }
Jeff Sharkey742e7902014-08-16 19:09:13 -0700499 }
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700500 }
501
502 @Override
Todd Kennedyeb9b0532016-03-08 10:10:54 -0800503 public void removeSplit(String splitName) {
504 if (TextUtils.isEmpty(params.appPackageName)) {
505 throw new IllegalStateException("Must specify package name to remove a split");
506 }
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000507
508 synchronized (mLock) {
509 assertCallerIsOwnerOrRootLocked();
510 assertPreparedAndNotCommittedOrDestroyedLocked("removeSplit");
511
512 try {
513 createRemoveSplitMarkerLocked(splitName);
514 } catch (IOException e) {
515 throw ExceptionUtils.wrap(e);
516 }
Todd Kennedyeb9b0532016-03-08 10:10:54 -0800517 }
518 }
519
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000520 private void createRemoveSplitMarkerLocked(String splitName) throws IOException {
Todd Kennedyeb9b0532016-03-08 10:10:54 -0800521 try {
522 final String markerName = splitName + REMOVE_SPLIT_MARKER_EXTENSION;
523 if (!FileUtils.isValidExtFilename(markerName)) {
524 throw new IllegalArgumentException("Invalid marker: " + markerName);
525 }
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000526 final File target = new File(resolveStageDirLocked(), markerName);
Todd Kennedyeb9b0532016-03-08 10:10:54 -0800527 target.createNewFile();
528 Os.chmod(target.getAbsolutePath(), 0 /*mode*/);
529 } catch (ErrnoException e) {
530 throw e.rethrowAsIOException();
531 }
532 }
533
534 @Override
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700535 public ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes) {
Jeff Sharkeya1031142014-07-12 18:09:46 -0700536 try {
537 return openWriteInternal(name, offsetBytes, lengthBytes);
538 } catch (IOException e) {
539 throw ExceptionUtils.wrap(e);
540 }
541 }
542
543 private ParcelFileDescriptor openWriteInternal(String name, long offsetBytes, long lengthBytes)
544 throws IOException {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700545 // Quick sanity check of state, and allocate a pipe for ourselves. We
546 // then do heavy disk allocation outside the lock, but this open pipe
547 // will block any attempted install transitions.
Jeff Sharkey02d4e342017-03-10 21:53:48 -0700548 final RevocableFileDescriptor fd;
Jeff Sharkey78cc3402014-05-22 10:52:49 -0700549 final FileBridge bridge;
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000550 final File stageDir;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700551 synchronized (mLock) {
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000552 assertCallerIsOwnerOrRootLocked();
553 assertPreparedAndNotSealedLocked("openWrite");
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700554
Jeff Sharkey02d4e342017-03-10 21:53:48 -0700555 if (PackageInstaller.ENABLE_REVOCABLE_FD) {
556 fd = new RevocableFileDescriptor();
557 bridge = null;
558 mFds.add(fd);
559 } else {
560 fd = null;
561 bridge = new FileBridge();
562 mBridges.add(bridge);
563 }
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000564
565 stageDir = resolveStageDirLocked();
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700566 }
567
568 try {
569 // Use installer provided name for now; we always rename later
570 if (!FileUtils.isValidExtFilename(name)) {
571 throw new IllegalArgumentException("Invalid name: " + name);
572 }
Shunta Sato4f26cb52016-06-28 09:29:19 +0900573 final File target;
574 final long identity = Binder.clearCallingIdentity();
575 try {
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000576 target = new File(stageDir, name);
Shunta Sato4f26cb52016-06-28 09:29:19 +0900577 } finally {
578 Binder.restoreCallingIdentity(identity);
579 }
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700580
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700581 // TODO: this should delegate to DCS so the system process avoids
582 // holding open FDs into containers.
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700583 final FileDescriptor targetFd = Libcore.os.open(target.getAbsolutePath(),
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700584 O_CREAT | O_WRONLY, 0644);
585 Os.chmod(target.getAbsolutePath(), 0644);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700586
587 // If caller specified a total length, allocate it for them. Free up
588 // cache space to grow, if needed.
Jeff Sharkeyd5d5e922017-02-21 10:51:23 -0700589 if (stageDir != null && lengthBytes > 0) {
Jeff Sharkey683bcd32017-03-18 17:54:51 -0600590 mContext.getSystemService(StorageManager.class).allocateBytes(targetFd, lengthBytes,
591 PackageHelper.translateAllocateFlags(params.installFlags));
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700592 }
593
594 if (offsetBytes > 0) {
595 Libcore.os.lseek(targetFd, offsetBytes, OsConstants.SEEK_SET);
596 }
597
Jeff Sharkey02d4e342017-03-10 21:53:48 -0700598 if (PackageInstaller.ENABLE_REVOCABLE_FD) {
599 fd.init(mContext, targetFd);
600 return fd.getRevocableFileDescriptor();
601 } else {
602 bridge.setTargetFile(targetFd);
603 bridge.start();
604 return new ParcelFileDescriptor(bridge.getClientSocket());
605 }
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700606
607 } catch (ErrnoException e) {
Jeff Sharkeya1031142014-07-12 18:09:46 -0700608 throw e.rethrowAsIOException();
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700609 }
610 }
611
612 @Override
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700613 public ParcelFileDescriptor openRead(String name) {
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000614 synchronized (mLock) {
615 assertCallerIsOwnerOrRootLocked();
616 assertPreparedAndNotCommittedOrDestroyedLocked("openRead");
617 try {
618 return openReadInternalLocked(name);
619 } catch (IOException e) {
620 throw ExceptionUtils.wrap(e);
621 }
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700622 }
623 }
624
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000625 private ParcelFileDescriptor openReadInternalLocked(String name) throws IOException {
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700626 try {
627 if (!FileUtils.isValidExtFilename(name)) {
628 throw new IllegalArgumentException("Invalid name: " + name);
629 }
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000630 final File target = new File(resolveStageDirLocked(), name);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700631 final FileDescriptor targetFd = Libcore.os.open(target.getAbsolutePath(), O_RDONLY, 0);
632 return new ParcelFileDescriptor(targetFd);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700633 } catch (ErrnoException e) {
634 throw e.rethrowAsIOException();
635 }
636 }
637
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000638 /**
639 * Check if the caller is the owner of this session. Otherwise throw a
640 * {@link SecurityException}.
641 */
642 private void assertCallerIsOwnerOrRootLocked() {
643 final int callingUid = Binder.getCallingUid();
644 if (callingUid != Process.ROOT_UID && callingUid != mInstallerUid) {
645 throw new SecurityException("Session does not belong to uid " + callingUid);
646 }
647 }
648
649 /**
650 * If anybody is reading or writing data of the session, throw an {@link SecurityException}.
651 */
652 private void assertNoWriteFileTransfersOpenLocked() {
653 // Verify that all writers are hands-off
654 for (RevocableFileDescriptor fd : mFds) {
655 if (!fd.isRevoked()) {
656 throw new SecurityException("Files still open");
657 }
658 }
659 for (FileBridge bridge : mBridges) {
660 if (!bridge.isClosed()) {
661 throw new SecurityException("Files still open");
662 }
663 }
664 }
665
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700666 @Override
Todd Kennedybeec8e22017-08-11 10:15:04 -0700667 public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
Jeff Sharkeya0907432014-08-15 10:23:11 -0700668 Preconditions.checkNotNull(statusReceiver);
669
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000670 // Cache package manager data without the lock held
671 final PackageInfo installedPkgInfo = mPm.getPackageInfo(
672 params.appPackageName, PackageManager.GET_SIGNATURES
673 | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
674
Jeff Sharkeycbf47912014-09-12 09:55:32 -0700675 final boolean wasSealed;
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700676 synchronized (mLock) {
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000677 assertCallerIsOwnerOrRootLocked();
678 assertPreparedAndNotDestroyedLocked("commit");
679
Todd Kennedybeec8e22017-08-11 10:15:04 -0700680 final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(
681 mContext, statusReceiver, sessionId, isInstallerDeviceOwnerLocked(), userId);
682 mRemoteObserver = adapter.getBinder();
683
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000684 if (forTransfer) {
685 mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, null);
686
687 if (mInstallerUid == mOriginalInstallerUid) {
688 throw new IllegalArgumentException("Session has not been transferred");
689 }
690 } else {
691 if (mInstallerUid != mOriginalInstallerUid) {
692 throw new IllegalArgumentException("Session has been transferred");
693 }
694 }
695
Jeff Sharkeycbf47912014-09-12 09:55:32 -0700696 wasSealed = mSealed;
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700697 if (!mSealed) {
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000698 try {
699 sealAndValidateLocked(installedPkgInfo);
700 } catch (PackageManagerException e) {
701 // Do now throw an exception here to stay compatible with O and older
702 destroyInternal();
703 dispatchSessionFinished(e.error, ExceptionUtils.getCompleteMessage(e), null);
704 return;
Jeff Sharkey02d4e342017-03-10 21:53:48 -0700705 }
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700706 }
Jeff Sharkeycbf47912014-09-12 09:55:32 -0700707
708 // Client staging is fully done at this point
709 mClientProgress = 1f;
710 computeProgressLocked(true);
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000711
712 // This ongoing commit should keep session active, even though client
713 // will probably close their end.
714 mActiveCount.incrementAndGet();
715
716 mCommitted = true;
Todd Kennedybeec8e22017-08-11 10:15:04 -0700717 mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700718 }
719
Jeff Sharkeycbf47912014-09-12 09:55:32 -0700720 if (!wasSealed) {
721 // Persist the fact that we've sealed ourselves to prevent
722 // mutations of any hard links we create. We do this without holding
723 // the session lock, since otherwise it's a lock inversion.
724 mCallback.onSessionSealedBlocking(this);
725 }
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700726 }
727
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000728 /**
729 * Seal the session to prevent further modification and validate the contents of it.
730 *
731 * <p>The session will be sealed after calling this method even if it failed.
732 *
733 * @param pkgInfo The package info for {@link #params}.packagename
734 */
Todd Kennedy544b3832017-08-22 10:48:18 -0700735 private void sealAndValidateLocked(@Nullable PackageInfo pkgInfo)
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000736 throws PackageManagerException {
737 assertNoWriteFileTransfersOpenLocked();
738
739 mSealed = true;
740
741 // Verify that stage looks sane with respect to existing application.
742 // This currently only ensures packageName, versionCode, and certificate
743 // consistency.
744 validateInstallLocked(pkgInfo);
745
746 // Read transfers from the original owner stay open, but as the session's data
747 // cannot be modified anymore, there is no leak of information.
748 }
749
750 @Override
751 public void transfer(String packageName) {
752 Preconditions.checkNotNull(packageName);
753
754 ApplicationInfo newOwnerAppInfo = mPm.getApplicationInfo(packageName, 0, userId);
755 if (newOwnerAppInfo == null) {
756 throw new ParcelableException(new PackageManager.NameNotFoundException(packageName));
757 }
758
759 if (PackageManager.PERMISSION_GRANTED != mPm.checkUidPermission(
760 Manifest.permission.INSTALL_PACKAGES, newOwnerAppInfo.uid)) {
761 throw new SecurityException("Destination package " + packageName + " does not have "
762 + "the " + Manifest.permission.INSTALL_PACKAGES + " permission");
763 }
764
765 // Cache package manager data without the lock held
766 final PackageInfo installedPkgInfo = mPm.getPackageInfo(
767 params.appPackageName, PackageManager.GET_SIGNATURES
768 | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
769
770 // Only install flags that can be verified by the app the session is transferred to are
771 // allowed. The parameters can be read via PackageInstaller.SessionInfo.
772 if (!params.areHiddenOptionsSet()) {
773 throw new SecurityException("Can only transfer sessions that use public options");
774 }
775
776 synchronized (mLock) {
777 assertCallerIsOwnerOrRootLocked();
778 assertPreparedAndNotSealedLocked("transfer");
779
780 try {
781 sealAndValidateLocked(installedPkgInfo);
782 } catch (PackageManagerException e) {
783 throw new IllegalArgumentException("Package is not valid", e);
784 }
785
786 if (!mPackageName.equals(mInstallerPackageName)) {
787 throw new SecurityException("Can only transfer sessions that update the original "
788 + "installer");
789 }
790
791 mInstallerPackageName = packageName;
792 mInstallerUid = newOwnerAppInfo.uid;
793 }
794
795 // Persist the fact that we've sealed ourselves to prevent
796 // mutations of any hard links we create. We do this without holding
797 // the session lock, since otherwise it's a lock inversion.
798 mCallback.onSessionSealedBlocking(this);
799 }
800
801 private void commitLocked()
Todd Kennedyf29d07a2016-08-08 15:17:43 -0700802 throws PackageManagerException {
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700803 if (mDestroyed) {
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700804 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed");
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700805 }
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700806 if (!mSealed) {
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700807 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed");
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700808 }
809
Jeff Sharkey742e7902014-08-16 19:09:13 -0700810 try {
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000811 resolveStageDirLocked();
Jeff Sharkey742e7902014-08-16 19:09:13 -0700812 } catch (IOException e) {
813 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700814 "Failed to resolve stage location", e);
Jeff Sharkey742e7902014-08-16 19:09:13 -0700815 }
816
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700817 Preconditions.checkNotNull(mPackageName);
Todd Kennedy373f0b42015-12-16 14:45:14 -0800818 Preconditions.checkNotNull(mSignatures);
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700819 Preconditions.checkNotNull(mResolvedBaseFile);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700820
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000821 if (needToAskForPermissionsLocked()) {
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700822 // User needs to accept permissions; give installer an intent they
823 // can use to involve user.
824 final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_PERMISSIONS);
Svet Ganovf1b7f202015-07-29 08:33:42 -0700825 intent.setPackage(mContext.getPackageManager().getPermissionControllerPackageName());
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700826 intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
827 try {
828 mRemoteObserver.onUserActionRequired(intent);
829 } catch (RemoteException ignored) {
830 }
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700831
832 // Commit was keeping session marked as active until now; release
833 // that extra refcount so session appears idle.
Philip P. Moltmannf46edf52017-08-08 10:44:34 -0700834 closeInternal(false);
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700835 return;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700836 }
837
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700838 if (stageCid != null) {
839 // Figure out the final installed size and resize the container once
840 // and for all. Internally the parser handles straddling between two
841 // locations when inheriting.
842 final long finalSize = calculateInstalledSize();
843 resizeContainer(stageCid, finalSize);
844 }
845
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700846 // Inherit any packages and native libraries from existing install that
847 // haven't been overridden.
Jeff Sharkeya0907432014-08-15 10:23:11 -0700848 if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700849 try {
Jeff Sharkey88d2a3c2014-11-22 16:49:34 -0800850 final List<File> fromFiles = mResolvedInheritedFiles;
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000851 final File toDir = resolveStageDirLocked();
Jeff Sharkey88d2a3c2014-11-22 16:49:34 -0800852
Narayan Kamathcd1fc142015-05-11 13:35:59 +0100853 if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles);
854 if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) {
855 throw new IllegalStateException("mInheritedFilesBase == null");
856 }
857
Jeff Sharkey88d2a3c2014-11-22 16:49:34 -0800858 if (isLinkPossible(fromFiles, toDir)) {
Narayan Kamathe845a1e2015-06-05 11:59:26 +0100859 if (!mResolvedInstructionSets.isEmpty()) {
860 final File oatDir = new File(toDir, "oat");
861 createOatDirs(mResolvedInstructionSets, oatDir);
862 }
863 linkFiles(fromFiles, toDir, mInheritedFilesBase);
Jeff Sharkey88d2a3c2014-11-22 16:49:34 -0800864 } else {
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700865 // TODO: this should delegate to DCS so the system process
866 // avoids holding open FDs into containers.
Jeff Sharkey88d2a3c2014-11-22 16:49:34 -0800867 copyFiles(fromFiles, toDir);
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700868 }
869 } catch (IOException e) {
870 throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
871 "Failed to inherit existing install", e);
872 }
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700873 }
874
Jeff Sharkeya1031142014-07-12 18:09:46 -0700875 // TODO: surface more granular state from dexopt
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700876 mInternalProgress = 0.5f;
877 computeProgressLocked(true);
Jeff Sharkeya1031142014-07-12 18:09:46 -0700878
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700879 // Unpack native libraries
880 extractNativeLibraries(mResolvedStageDir, params.abiOverride);
881
882 // Container is ready to go, let's seal it up!
883 if (stageCid != null) {
884 finalizeAndFixContainer(stageCid);
885 }
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700886
887 // We've reached point of no return; call into PMS to install the stage.
888 // Regardless of success or failure we always destroy session.
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700889 final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() {
890 @Override
Jeff Sharkeyfbd0e9f2014-08-06 16:34:34 -0700891 public void onUserActionRequired(Intent intent) {
892 throw new IllegalStateException();
893 }
894
895 @Override
896 public void onPackageInstalled(String basePackageName, int returnCode, String msg,
897 Bundle extras) {
Jeff Sharkeya1031142014-07-12 18:09:46 -0700898 destroyInternal();
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700899 dispatchSessionFinished(returnCode, msg, extras);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700900 }
901 };
902
Jeff Sharkeye9808042014-09-11 21:15:37 -0700903 final UserHandle user;
904 if ((params.installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
905 user = UserHandle.ALL;
906 } else {
907 user = new UserHandle(userId);
908 }
909
Jeff Sharkey497c0522015-05-12 13:07:14 -0700910 mRelinquished = true;
Jeff Sharkeye9808042014-09-11 21:15:37 -0700911 mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params,
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000912 mInstallerPackageName, mInstallerUid, user, mCertificates);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700913 }
914
915 /**
916 * Validate install by confirming that all application packages are have
917 * consistent package name, version code, and signing certificates.
918 * <p>
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700919 * Clears and populates {@link #mResolvedBaseFile},
920 * {@link #mResolvedStagedFiles}, and {@link #mResolvedInheritedFiles}.
921 * <p>
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700922 * Renames package files in stage to match split names defined inside.
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -0700923 * <p>
924 * Note that upgrade compatibility is still performed by
925 * {@link PackageManagerService}.
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700926 */
Todd Kennedy544b3832017-08-22 10:48:18 -0700927 private void validateInstallLocked(@Nullable PackageInfo pkgInfo)
928 throws PackageManagerException {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700929 mPackageName = null;
930 mVersionCode = -1;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700931 mSignatures = null;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700932
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700933 mResolvedBaseFile = null;
934 mResolvedStagedFiles.clear();
935 mResolvedInheritedFiles.clear();
936
Todd Kennedyeb9b0532016-03-08 10:10:54 -0800937 final File[] removedFiles = mResolvedStageDir.listFiles(sRemovedFilter);
938 final List<String> removeSplitList = new ArrayList<>();
939 if (!ArrayUtils.isEmpty(removedFiles)) {
940 for (File removedFile : removedFiles) {
941 final String fileName = removedFile.getName();
942 final String splitName = fileName.substring(
943 0, fileName.length() - REMOVE_SPLIT_MARKER_EXTENSION.length());
944 removeSplitList.add(splitName);
945 }
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700946 }
947
Todd Kennedyeb9b0532016-03-08 10:10:54 -0800948 final File[] addedFiles = mResolvedStageDir.listFiles(sAddedFilter);
949 if (ArrayUtils.isEmpty(addedFiles) && removeSplitList.size() == 0) {
950 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged");
951 }
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700952 // Verify that all staged packages are internally consistent
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700953 final ArraySet<String> stagedSplits = new ArraySet<>();
Todd Kennedyeb9b0532016-03-08 10:10:54 -0800954 for (File addedFile : addedFiles) {
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700955 final ApkLite apk;
Jeff Sharkey275e0852014-06-17 18:18:49 -0700956 try {
Alex Klyubin58bfae42017-01-03 16:25:33 -0800957 int flags = PackageParser.PARSE_COLLECT_CERTIFICATES;
Todd Kennedybe0b8892017-02-15 14:13:52 -0800958 if ((params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
Alex Klyubin58bfae42017-01-03 16:25:33 -0800959 flags |= PackageParser.PARSE_IS_EPHEMERAL;
960 }
961 apk = PackageParser.parseApkLite(addedFile, flags);
Jeff Sharkey275e0852014-06-17 18:18:49 -0700962 } catch (PackageParserException e) {
Jeff Sharkeybc097552014-09-09 14:57:26 -0700963 throw PackageManagerException.from(e);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700964 }
965
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700966 if (!stagedSplits.add(apk.splitName)) {
Jeff Sharkeye0b0bef2014-07-12 15:37:47 -0700967 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700968 "Split " + apk.splitName + " was defined multiple times");
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700969 }
970
971 // Use first package to define unknown values
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700972 if (mPackageName == null) {
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700973 mPackageName = apk.packageName;
974 mVersionCode = apk.versionCode;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700975 }
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700976 if (mSignatures == null) {
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700977 mSignatures = apk.signatures;
Todd Kennedyd9d438a2016-04-06 14:08:14 -0700978 mCertificates = apk.certificates;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700979 }
980
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000981 assertApkConsistentLocked(String.valueOf(addedFile), apk);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700982
983 // Take this opportunity to enforce uniform naming
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700984 final String targetName;
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700985 if (apk.splitName == null) {
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700986 targetName = "base.apk";
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700987 } else {
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700988 targetName = "split_" + apk.splitName + ".apk";
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700989 }
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700990 if (!FileUtils.isValidExtFilename(targetName)) {
Jeff Sharkeye0b0bef2014-07-12 15:37:47 -0700991 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700992 "Invalid filename: " + targetName);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700993 }
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700994
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700995 final File targetFile = new File(mResolvedStageDir, targetName);
Todd Kennedyeb9b0532016-03-08 10:10:54 -0800996 if (!addedFile.equals(targetFile)) {
997 addedFile.renameTo(targetFile);
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700998 }
999
1000 // Base is coming from session
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001001 if (apk.splitName == null) {
1002 mResolvedBaseFile = targetFile;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -07001003 }
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001004
1005 mResolvedStagedFiles.add(targetFile);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -07001006 }
1007
Todd Kennedyeb9b0532016-03-08 10:10:54 -08001008 if (removeSplitList.size() > 0) {
Todd Kennedy544b3832017-08-22 10:48:18 -07001009 if (pkgInfo == null) {
1010 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1011 "Missing existing base package for " + mPackageName);
1012 }
1013
Todd Kennedyeb9b0532016-03-08 10:10:54 -08001014 // validate split names marked for removal
Todd Kennedyeb9b0532016-03-08 10:10:54 -08001015 for (String splitName : removeSplitList) {
Todd Kennedyf29d07a2016-08-08 15:17:43 -07001016 if (!ArrayUtils.contains(pkgInfo.splitNames, splitName)) {
Todd Kennedyeb9b0532016-03-08 10:10:54 -08001017 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1018 "Split not found: " + splitName);
1019 }
1020 }
1021
1022 // ensure we've got appropriate package name, version code and signatures
1023 if (mPackageName == null) {
Todd Kennedyf29d07a2016-08-08 15:17:43 -07001024 mPackageName = pkgInfo.packageName;
1025 mVersionCode = pkgInfo.versionCode;
Todd Kennedyeb9b0532016-03-08 10:10:54 -08001026 }
1027 if (mSignatures == null) {
Todd Kennedyf29d07a2016-08-08 15:17:43 -07001028 mSignatures = pkgInfo.signatures;
Todd Kennedyeb9b0532016-03-08 10:10:54 -08001029 }
1030 }
1031
Jeff Sharkeya0907432014-08-15 10:23:11 -07001032 if (params.mode == SessionParams.MODE_FULL_INSTALL) {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -07001033 // Full installs must include a base package
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001034 if (!stagedSplits.contains(null)) {
Jeff Sharkeye0b0bef2014-07-12 15:37:47 -07001035 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -07001036 "Full install must include a base package");
1037 }
1038
1039 } else {
Jeff Sharkey7328a1b2014-08-07 14:01:43 -07001040 // Partial installs must be consistent with existing install
Philip P. Moltmann7460c592017-08-08 20:07:11 +00001041 if (pkgInfo == null || pkgInfo.applicationInfo == null) {
Jeff Sharkeye0b0bef2014-07-12 15:37:47 -07001042 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -07001043 "Missing existing base package for " + mPackageName);
1044 }
1045
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001046 final PackageLite existing;
1047 final ApkLite existingBase;
Philip P. Moltmann7460c592017-08-08 20:07:11 +00001048 ApplicationInfo appInfo = pkgInfo.applicationInfo;
Jeff Sharkey275e0852014-06-17 18:18:49 -07001049 try {
Todd Kennedyf29d07a2016-08-08 15:17:43 -07001050 existing = PackageParser.parsePackageLite(new File(appInfo.getCodePath()), 0);
1051 existingBase = PackageParser.parseApkLite(new File(appInfo.getBaseCodePath()),
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -07001052 PackageParser.PARSE_COLLECT_CERTIFICATES);
Jeff Sharkey275e0852014-06-17 18:18:49 -07001053 } catch (PackageParserException e) {
Jeff Sharkeybc097552014-09-09 14:57:26 -07001054 throw PackageManagerException.from(e);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -07001055 }
1056
Philip P. Moltmann7460c592017-08-08 20:07:11 +00001057 assertApkConsistentLocked("Existing base", existingBase);
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001058
1059 // Inherit base if not overridden
1060 if (mResolvedBaseFile == null) {
Todd Kennedyf29d07a2016-08-08 15:17:43 -07001061 mResolvedBaseFile = new File(appInfo.getBaseCodePath());
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001062 mResolvedInheritedFiles.add(mResolvedBaseFile);
1063 }
1064
1065 // Inherit splits if not overridden
1066 if (!ArrayUtils.isEmpty(existing.splitNames)) {
1067 for (int i = 0; i < existing.splitNames.length; i++) {
1068 final String splitName = existing.splitNames[i];
1069 final File splitFile = new File(existing.splitCodePaths[i]);
Todd Kennedyeb9b0532016-03-08 10:10:54 -08001070 final boolean splitRemoved = removeSplitList.contains(splitName);
1071 if (!stagedSplits.contains(splitName) && !splitRemoved) {
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001072 mResolvedInheritedFiles.add(splitFile);
1073 }
1074 }
1075 }
Narayan Kamathcd1fc142015-05-11 13:35:59 +01001076
1077 // Inherit compiled oat directory.
Todd Kennedyf29d07a2016-08-08 15:17:43 -07001078 final File packageInstallDir = (new File(appInfo.getBaseCodePath())).getParentFile();
Narayan Kamathcd1fc142015-05-11 13:35:59 +01001079 mInheritedFilesBase = packageInstallDir;
1080 final File oatDir = new File(packageInstallDir, "oat");
1081 if (oatDir.exists()) {
1082 final File[] archSubdirs = oatDir.listFiles();
Narayan Kamathcd1fc142015-05-11 13:35:59 +01001083
Narayan Kamathe845a1e2015-06-05 11:59:26 +01001084 // Keep track of all instruction sets we've seen compiled output for.
1085 // If we're linking (and not copying) inherited files, we can recreate the
1086 // instruction set hierarchy and link compiled output.
1087 if (archSubdirs != null && archSubdirs.length > 0) {
1088 final String[] instructionSets = InstructionSets.getAllDexCodeInstructionSets();
1089 for (File archSubDir : archSubdirs) {
1090 // Skip any directory that isn't an ISA subdir.
1091 if (!ArrayUtils.contains(instructionSets, archSubDir.getName())) {
1092 continue;
1093 }
1094
1095 mResolvedInstructionSets.add(archSubDir.getName());
1096 List<File> oatFiles = Arrays.asList(archSubDir.listFiles());
Jeff Haod1235f52017-06-13 11:09:10 -07001097
1098 // Only add compiled files associated with the base.
1099 // Once b/62269291 is resolved, we can add all compiled files again.
1100 for (File oatFile : oatFiles) {
1101 if (oatFile.getName().equals("base.art")
1102 || oatFile.getName().equals("base.odex")
1103 || oatFile.getName().equals("base.vdex")) {
1104 mResolvedInheritedFiles.add(oatFile);
1105 }
Narayan Kamathe845a1e2015-06-05 11:59:26 +01001106 }
Narayan Kamathcd1fc142015-05-11 13:35:59 +01001107 }
1108 }
1109 }
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -07001110 }
1111 }
1112
Philip P. Moltmann7460c592017-08-08 20:07:11 +00001113 private void assertApkConsistentLocked(String tag, ApkLite apk)
Todd Kennedyf29d07a2016-08-08 15:17:43 -07001114 throws PackageManagerException {
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001115 if (!mPackageName.equals(apk.packageName)) {
Jeff Sharkeye0b0bef2014-07-12 15:37:47 -07001116 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " package "
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001117 + apk.packageName + " inconsistent with " + mPackageName);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -07001118 }
Todd Kennedyeb9b0532016-03-08 10:10:54 -08001119 if (params.appPackageName != null && !params.appPackageName.equals(apk.packageName)) {
1120 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag
1121 + " specified package " + params.appPackageName
1122 + " inconsistent with " + apk.packageName);
1123 }
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001124 if (mVersionCode != apk.versionCode) {
Jeff Sharkeye0b0bef2014-07-12 15:37:47 -07001125 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001126 + " version code " + apk.versionCode + " inconsistent with "
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -07001127 + mVersionCode);
1128 }
Todd Kennedy373f0b42015-12-16 14:45:14 -08001129 if (!Signature.areExactMatch(mSignatures, apk.signatures)) {
Jeff Sharkeye0b0bef2014-07-12 15:37:47 -07001130 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -07001131 tag + " signatures are inconsistent");
1132 }
1133 }
1134
1135 /**
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001136 * Calculate the final install footprint size, combining both staged and
1137 * existing APKs together and including unpacked native code from both.
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -07001138 */
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001139 private long calculateInstalledSize() throws PackageManagerException {
1140 Preconditions.checkNotNull(mResolvedBaseFile);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -07001141
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001142 final ApkLite baseApk;
1143 try {
1144 baseApk = PackageParser.parseApkLite(mResolvedBaseFile, 0);
1145 } catch (PackageParserException e) {
Jeff Sharkeybc097552014-09-09 14:57:26 -07001146 throw PackageManagerException.from(e);
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001147 }
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -07001148
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001149 final List<String> splitPaths = new ArrayList<>();
1150 for (File file : mResolvedStagedFiles) {
1151 if (mResolvedBaseFile.equals(file)) continue;
1152 splitPaths.add(file.getAbsolutePath());
1153 }
1154 for (File file : mResolvedInheritedFiles) {
1155 if (mResolvedBaseFile.equals(file)) continue;
1156 splitPaths.add(file.getAbsolutePath());
1157 }
1158
1159 // This is kind of hacky; we're creating a half-parsed package that is
1160 // straddled between the inherited and staged APKs.
Adam Lesinski1665d0f2017-03-10 14:46:57 -08001161 final PackageLite pkg = new PackageLite(null, baseApk, null, null, null, null,
Narayan Kamath96c11c52017-08-09 13:07:21 +01001162 splitPaths.toArray(new String[splitPaths.size()]), null);
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001163 final boolean isForwardLocked =
1164 (params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
1165
1166 try {
1167 return PackageHelper.calculateInstalledSize(pkg, isForwardLocked, params.abiOverride);
1168 } catch (IOException e) {
1169 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1170 "Failed to calculate install size", e);
1171 }
1172 }
1173
Jeff Sharkey88d2a3c2014-11-22 16:49:34 -08001174 /**
1175 * Determine if creating hard links between source and destination is
1176 * possible. That is, do they all live on the same underlying device.
1177 */
1178 private boolean isLinkPossible(List<File> fromFiles, File toDir) {
1179 try {
1180 final StructStat toStat = Os.stat(toDir.getAbsolutePath());
1181 for (File fromFile : fromFiles) {
1182 final StructStat fromStat = Os.stat(fromFile.getAbsolutePath());
1183 if (fromStat.st_dev != toStat.st_dev) {
1184 return false;
1185 }
1186 }
1187 } catch (ErrnoException e) {
1188 Slog.w(TAG, "Failed to detect if linking possible: " + e);
1189 return false;
1190 }
1191 return true;
1192 }
1193
Philip P. Moltmann7460c592017-08-08 20:07:11 +00001194 /**
1195 * @return the uid of the owner this session
1196 */
1197 public int getInstallerUid() {
1198 synchronized (mLock) {
1199 return mInstallerUid;
1200 }
1201 }
1202
Narayan Kamathe845a1e2015-06-05 11:59:26 +01001203 private static String getRelativePath(File file, File base) throws IOException {
Narayan Kamathcd1fc142015-05-11 13:35:59 +01001204 final String pathStr = file.getAbsolutePath();
Narayan Kamathe845a1e2015-06-05 11:59:26 +01001205 final String baseStr = base.getAbsolutePath();
Narayan Kamathcd1fc142015-05-11 13:35:59 +01001206 // Don't allow relative paths.
1207 if (pathStr.contains("/.") ) {
1208 throw new IOException("Invalid path (was relative) : " + pathStr);
1209 }
1210
Narayan Kamathe845a1e2015-06-05 11:59:26 +01001211 if (pathStr.startsWith(baseStr)) {
1212 return pathStr.substring(baseStr.length());
Narayan Kamathcd1fc142015-05-11 13:35:59 +01001213 }
1214
Narayan Kamathe845a1e2015-06-05 11:59:26 +01001215 throw new IOException("File: " + pathStr + " outside base: " + baseStr);
Narayan Kamathcd1fc142015-05-11 13:35:59 +01001216 }
1217
Jeff Sharkeyfdeeeea2016-01-11 17:34:24 -07001218 private void createOatDirs(List<String> instructionSets, File fromDir)
1219 throws PackageManagerException {
Narayan Kamathe845a1e2015-06-05 11:59:26 +01001220 for (String instructionSet : instructionSets) {
Jeff Sharkeyfdeeeea2016-01-11 17:34:24 -07001221 try {
1222 mPm.mInstaller.createOatDir(fromDir.getAbsolutePath(), instructionSet);
1223 } catch (InstallerException e) {
1224 throw PackageManagerException.from(e);
1225 }
Narayan Kamathe845a1e2015-06-05 11:59:26 +01001226 }
1227 }
1228
1229 private void linkFiles(List<File> fromFiles, File toDir, File fromDir)
Narayan Kamathcd1fc142015-05-11 13:35:59 +01001230 throws IOException {
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001231 for (File fromFile : fromFiles) {
Narayan Kamathe845a1e2015-06-05 11:59:26 +01001232 final String relativePath = getRelativePath(fromFile, fromDir);
Jeff Sharkeyfdeeeea2016-01-11 17:34:24 -07001233 try {
1234 mPm.mInstaller.linkFile(relativePath, fromDir.getAbsolutePath(),
1235 toDir.getAbsolutePath());
1236 } catch (InstallerException e) {
Narayan Kamathe845a1e2015-06-05 11:59:26 +01001237 throw new IOException("failed linkOrCreateDir(" + relativePath + ", "
Jeff Sharkeyfdeeeea2016-01-11 17:34:24 -07001238 + fromDir + ", " + toDir + ")", e);
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001239 }
1240 }
Narayan Kamathe845a1e2015-06-05 11:59:26 +01001241
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001242 Slog.d(TAG, "Linked " + fromFiles.size() + " files into " + toDir);
1243 }
1244
1245 private static void copyFiles(List<File> fromFiles, File toDir) throws IOException {
1246 // Remove any partial files from previous attempt
1247 for (File file : toDir.listFiles()) {
1248 if (file.getName().endsWith(".tmp")) {
1249 file.delete();
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -07001250 }
1251 }
Jeff Sharkey9a445772014-07-16 11:32:08 -07001252
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001253 for (File fromFile : fromFiles) {
1254 final File tmpFile = File.createTempFile("inherit", ".tmp", toDir);
1255 if (LOGD) Slog.d(TAG, "Copying " + fromFile + " to " + tmpFile);
1256 if (!FileUtils.copyFile(fromFile, tmpFile)) {
1257 throw new IOException("Failed to copy " + fromFile + " to " + tmpFile);
1258 }
Jeff Sharkey88d2a3c2014-11-22 16:49:34 -08001259 try {
1260 Os.chmod(tmpFile.getAbsolutePath(), 0644);
1261 } catch (ErrnoException e) {
1262 throw new IOException("Failed to chmod " + tmpFile);
1263 }
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001264 final File toFile = new File(toDir, fromFile.getName());
1265 if (LOGD) Slog.d(TAG, "Renaming " + tmpFile + " to " + toFile);
1266 if (!tmpFile.renameTo(toFile)) {
1267 throw new IOException("Failed to rename " + tmpFile + " to " + toFile);
1268 }
1269 }
1270 Slog.d(TAG, "Copied " + fromFiles.size() + " files into " + toDir);
1271 }
1272
1273 private static void extractNativeLibraries(File packageDir, String abiOverride)
1274 throws PackageManagerException {
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001275 // Always start from a clean slate
1276 final File libDir = new File(packageDir, NativeLibraryHelper.LIB_DIR_NAME);
1277 NativeLibraryHelper.removeNativeBinariesFromDirLI(libDir, true);
1278
1279 NativeLibraryHelper.Handle handle = null;
1280 try {
1281 handle = NativeLibraryHelper.Handle.create(packageDir);
1282 final int res = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libDir,
1283 abiOverride);
1284 if (res != PackageManager.INSTALL_SUCCEEDED) {
1285 throw new PackageManagerException(res,
1286 "Failed to extract native libraries, res=" + res);
1287 }
1288 } catch (IOException e) {
1289 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
1290 "Failed to extract native libraries", e);
1291 } finally {
1292 IoUtils.closeQuietly(handle);
1293 }
1294 }
1295
1296 private static void resizeContainer(String cid, long targetSize)
1297 throws PackageManagerException {
1298 String path = PackageHelper.getSdDir(cid);
1299 if (path == null) {
1300 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
1301 "Failed to find mounted " + cid);
1302 }
1303
1304 final long currentSize = new File(path).getTotalSpace();
1305 if (currentSize > targetSize) {
1306 Slog.w(TAG, "Current size " + currentSize + " is larger than target size "
1307 + targetSize + "; skipping resize");
1308 return;
1309 }
1310
1311 if (!PackageHelper.unMountSdDir(cid)) {
1312 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
1313 "Failed to unmount " + cid + " before resize");
1314 }
1315
1316 if (!PackageHelper.resizeSdDir(targetSize, cid,
1317 PackageManagerService.getEncryptKey())) {
1318 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
1319 "Failed to resize " + cid + " to " + targetSize + " bytes");
1320 }
1321
1322 path = PackageHelper.mountSdDir(cid, PackageManagerService.getEncryptKey(),
1323 Process.SYSTEM_UID, false);
1324 if (path == null) {
1325 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
1326 "Failed to mount " + cid + " after resize");
1327 }
1328 }
1329
1330 private void finalizeAndFixContainer(String cid) throws PackageManagerException {
1331 if (!PackageHelper.finalizeSdDir(cid)) {
1332 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
1333 "Failed to finalize container " + cid);
1334 }
1335
Todd Kennedyf29d07a2016-08-08 15:17:43 -07001336 if (!PackageHelper.fixSdPermissions(cid, defaultContainerGid, null)) {
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001337 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
1338 "Failed to fix permissions on container " + cid);
1339 }
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -07001340 }
1341
Jeff Sharkey7328a1b2014-08-07 14:01:43 -07001342 void setPermissionsResult(boolean accepted) {
1343 if (!mSealed) {
1344 throw new SecurityException("Must be sealed to accept permissions");
1345 }
1346
1347 if (accepted) {
1348 // Mark and kick off another install pass
Todd Kennedya1d12cf2015-09-29 15:43:00 -07001349 synchronized (mLock) {
Philip P. Moltmann7460c592017-08-08 20:07:11 +00001350 mPermissionsManuallyAccepted = true;
1351 mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
Todd Kennedya1d12cf2015-09-29 15:43:00 -07001352 }
Jeff Sharkey7328a1b2014-08-07 14:01:43 -07001353 } else {
1354 destroyInternal();
Jeff Sharkeyf0600952014-08-07 17:31:53 -07001355 dispatchSessionFinished(INSTALL_FAILED_ABORTED, "User rejected permissions", null);
Jeff Sharkey7328a1b2014-08-07 14:01:43 -07001356 }
1357 }
1358
Jeff Sharkey77d218e2014-09-06 12:20:37 -07001359 public void open() throws IOException {
1360 if (mActiveCount.getAndIncrement() == 0) {
Jeff Sharkeybc7bce32014-09-05 15:53:05 -07001361 mCallback.onSessionActiveChanged(this, true);
Jeff Sharkey742e7902014-08-16 19:09:13 -07001362 }
Jeff Sharkey77d218e2014-09-06 12:20:37 -07001363
Philip P. Moltmann7460c592017-08-08 20:07:11 +00001364 boolean wasPrepared;
Jeff Sharkey77d218e2014-09-06 12:20:37 -07001365 synchronized (mLock) {
Philip P. Moltmann7460c592017-08-08 20:07:11 +00001366 wasPrepared = mPrepared;
Jeff Sharkey77d218e2014-09-06 12:20:37 -07001367 if (!mPrepared) {
1368 if (stageDir != null) {
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -07001369 prepareStageDir(stageDir);
Jeff Sharkey77d218e2014-09-06 12:20:37 -07001370 } else if (stageCid != null) {
Shunta Sato4f26cb52016-06-28 09:29:19 +09001371 final long identity = Binder.clearCallingIdentity();
1372 try {
1373 prepareExternalStageCid(stageCid, params.sizeBytes);
1374 } finally {
1375 Binder.restoreCallingIdentity(identity);
1376 }
Jeff Sharkey77d218e2014-09-06 12:20:37 -07001377
1378 // TODO: deliver more granular progress for ASEC allocation
1379 mInternalProgress = 0.25f;
1380 computeProgressLocked(true);
1381 } else {
1382 throw new IllegalArgumentException(
1383 "Exactly one of stageDir or stageCid stage must be set");
1384 }
1385
1386 mPrepared = true;
Jeff Sharkey77d218e2014-09-06 12:20:37 -07001387 }
1388 }
Philip P. Moltmann7460c592017-08-08 20:07:11 +00001389
1390 if (!wasPrepared) {
1391 mCallback.onSessionPrepared(this);
1392 }
Jeff Sharkey742e7902014-08-16 19:09:13 -07001393 }
1394
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -07001395 @Override
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -07001396 public void close() {
Philip P. Moltmannf46edf52017-08-08 10:44:34 -07001397 closeInternal(true);
1398 }
1399
1400 private void closeInternal(boolean checkCaller) {
Philip P. Moltmann3306c0d2017-08-08 09:19:23 -07001401 int activeCount;
Philip P. Moltmann7460c592017-08-08 20:07:11 +00001402 synchronized (mLock) {
Philip P. Moltmannf46edf52017-08-08 10:44:34 -07001403 if (checkCaller) {
1404 assertCallerIsOwnerOrRootLocked();
1405 }
1406
Philip P. Moltmann3306c0d2017-08-08 09:19:23 -07001407 activeCount = mActiveCount.decrementAndGet();
Philip P. Moltmann7460c592017-08-08 20:07:11 +00001408 }
1409
Philip P. Moltmann3306c0d2017-08-08 09:19:23 -07001410 if (activeCount == 0) {
Jeff Sharkeybc7bce32014-09-05 15:53:05 -07001411 mCallback.onSessionActiveChanged(this, false);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001412 }
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -07001413 }
1414
1415 @Override
1416 public void abandon() {
Philip P. Moltmann7460c592017-08-08 20:07:11 +00001417 synchronized (mLock) {
1418 assertCallerIsOwnerOrRootLocked();
1419
1420 if (mRelinquished) {
1421 Slog.d(TAG, "Ignoring abandon after commit relinquished control");
1422 return;
1423 }
1424 destroyInternal();
Jeff Sharkey497c0522015-05-12 13:07:14 -07001425 }
Philip P. Moltmann7460c592017-08-08 20:07:11 +00001426
Jeff Sharkeyf0600952014-08-07 17:31:53 -07001427 dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null);
Jeff Sharkey7328a1b2014-08-07 14:01:43 -07001428 }
1429
1430 private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) {
Todd Kennedybeec8e22017-08-11 10:15:04 -07001431 final IPackageInstallObserver2 observer;
1432 final String packageName;
Philip P. Moltmann7460c592017-08-08 20:07:11 +00001433 synchronized (mLock) {
1434 mFinalStatus = returnCode;
1435 mFinalMessage = msg;
Jeff Sharkey7328a1b2014-08-07 14:01:43 -07001436
Philip P. Moltmann7460c592017-08-08 20:07:11 +00001437 observer = mRemoteObserver;
1438 packageName = mPackageName;
1439 }
1440
1441 if (observer != null) {
Jeff Sharkey7328a1b2014-08-07 14:01:43 -07001442 try {
Philip P. Moltmann7460c592017-08-08 20:07:11 +00001443 observer.onPackageInstalled(packageName, returnCode, msg, extras);
Jeff Sharkey7328a1b2014-08-07 14:01:43 -07001444 } catch (RemoteException ignored) {
1445 }
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -07001446 }
Jeff Sharkey7328a1b2014-08-07 14:01:43 -07001447
1448 final boolean success = (returnCode == PackageManager.INSTALL_SUCCEEDED);
Sunny Goyal6d7cb232017-01-30 10:43:18 -08001449
1450 // Send broadcast to default launcher only if it's a new install
1451 final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING);
1452 if (success && isNewInstall) {
Sunny Goyala31a74b2017-05-11 15:59:19 -07001453 mPm.sendSessionCommitBroadcast(generateInfo(), userId);
Sunny Goyal6d7cb232017-01-30 10:43:18 -08001454 }
1455
Jeff Sharkey7328a1b2014-08-07 14:01:43 -07001456 mCallback.onSessionFinished(this, success);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -07001457 }
Jeff Sharkeya1031142014-07-12 18:09:46 -07001458
1459 private void destroyInternal() {
1460 synchronized (mLock) {
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001461 mSealed = true;
1462 mDestroyed = true;
Jeff Sharkey77d218e2014-09-06 12:20:37 -07001463
1464 // Force shut down all bridges
Jeff Sharkey02d4e342017-03-10 21:53:48 -07001465 for (RevocableFileDescriptor fd : mFds) {
1466 fd.revoke();
1467 }
Jeff Sharkey77d218e2014-09-06 12:20:37 -07001468 for (FileBridge bridge : mBridges) {
1469 bridge.forceClose();
1470 }
Jeff Sharkeya1031142014-07-12 18:09:46 -07001471 }
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001472 if (stageDir != null) {
Jeff Sharkeyfdeeeea2016-01-11 17:34:24 -07001473 try {
1474 mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
1475 } catch (InstallerException ignored) {
1476 }
Jeff Sharkey742e7902014-08-16 19:09:13 -07001477 }
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001478 if (stageCid != null) {
1479 PackageHelper.destroySdDir(stageCid);
Jeff Sharkey742e7902014-08-16 19:09:13 -07001480 }
Jeff Sharkeya1031142014-07-12 18:09:46 -07001481 }
1482
1483 void dump(IndentingPrintWriter pw) {
Jeff Sharkey742e7902014-08-16 19:09:13 -07001484 synchronized (mLock) {
1485 dumpLocked(pw);
1486 }
1487 }
1488
1489 private void dumpLocked(IndentingPrintWriter pw) {
Jeff Sharkeya1031142014-07-12 18:09:46 -07001490 pw.println("Session " + sessionId + ":");
1491 pw.increaseIndent();
1492
1493 pw.printPair("userId", userId);
Philip P. Moltmann7460c592017-08-08 20:07:11 +00001494 pw.printPair("mOriginalInstallerUid", mOriginalInstallerUid);
1495 pw.printPair("mInstallerPackageName", mInstallerPackageName);
1496 pw.printPair("mInstallerUid", mInstallerUid);
Jeff Sharkeya1031142014-07-12 18:09:46 -07001497 pw.printPair("createdMillis", createdMillis);
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001498 pw.printPair("stageDir", stageDir);
1499 pw.printPair("stageCid", stageCid);
Jeff Sharkeya1031142014-07-12 18:09:46 -07001500 pw.println();
1501
1502 params.dump(pw);
1503
1504 pw.printPair("mClientProgress", mClientProgress);
1505 pw.printPair("mProgress", mProgress);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001506 pw.printPair("mSealed", mSealed);
Philip P. Moltmann7460c592017-08-08 20:07:11 +00001507 pw.printPair("mPermissionsManuallyAccepted", mPermissionsManuallyAccepted);
Jeff Sharkey497c0522015-05-12 13:07:14 -07001508 pw.printPair("mRelinquished", mRelinquished);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001509 pw.printPair("mDestroyed", mDestroyed);
Jeff Sharkey02d4e342017-03-10 21:53:48 -07001510 pw.printPair("mFds", mFds.size());
Jeff Sharkeya1031142014-07-12 18:09:46 -07001511 pw.printPair("mBridges", mBridges.size());
Jeff Sharkey7328a1b2014-08-07 14:01:43 -07001512 pw.printPair("mFinalStatus", mFinalStatus);
1513 pw.printPair("mFinalMessage", mFinalMessage);
Jeff Sharkeya1031142014-07-12 18:09:46 -07001514 pw.println();
1515
1516 pw.decreaseIndent();
1517 }
Philip P. Moltmann7460c592017-08-08 20:07:11 +00001518
1519 private static void writeGrantedRuntimePermissionsLocked(XmlSerializer out,
1520 String[] grantedRuntimePermissions) throws IOException {
1521 if (grantedRuntimePermissions != null) {
1522 for (String permission : grantedRuntimePermissions) {
1523 out.startTag(null, TAG_GRANTED_RUNTIME_PERMISSION);
1524 writeStringAttribute(out, ATTR_NAME, permission);
1525 out.endTag(null, TAG_GRANTED_RUNTIME_PERMISSION);
1526 }
1527 }
1528 }
1529
1530 private static File buildAppIconFile(int sessionId, @NonNull File sessionsDir) {
1531 return new File(sessionsDir, "app_icon." + sessionId + ".png");
1532 }
1533
1534 /**
1535 * Write this session to a {@link XmlSerializer}.
1536 *
1537 * @param out Where to write the session to
1538 * @param sessionsDir The directory containing the sessions
1539 */
1540 void write(@NonNull XmlSerializer out, @NonNull File sessionsDir) throws IOException {
1541 synchronized (mLock) {
1542 out.startTag(null, TAG_SESSION);
1543
1544 writeIntAttribute(out, ATTR_SESSION_ID, sessionId);
1545 writeIntAttribute(out, ATTR_USER_ID, userId);
1546 writeStringAttribute(out, ATTR_INSTALLER_PACKAGE_NAME,
1547 mInstallerPackageName);
1548 writeIntAttribute(out, ATTR_INSTALLER_UID, mInstallerUid);
1549 writeLongAttribute(out, ATTR_CREATED_MILLIS, createdMillis);
1550 if (stageDir != null) {
1551 writeStringAttribute(out, ATTR_SESSION_STAGE_DIR,
1552 stageDir.getAbsolutePath());
1553 }
1554 if (stageCid != null) {
1555 writeStringAttribute(out, ATTR_SESSION_STAGE_CID, stageCid);
1556 }
1557 writeBooleanAttribute(out, ATTR_PREPARED, isPrepared());
1558 writeBooleanAttribute(out, ATTR_SEALED, isSealed());
1559
1560 writeIntAttribute(out, ATTR_MODE, params.mode);
1561 writeIntAttribute(out, ATTR_INSTALL_FLAGS, params.installFlags);
1562 writeIntAttribute(out, ATTR_INSTALL_LOCATION, params.installLocation);
1563 writeLongAttribute(out, ATTR_SIZE_BYTES, params.sizeBytes);
1564 writeStringAttribute(out, ATTR_APP_PACKAGE_NAME, params.appPackageName);
1565 writeStringAttribute(out, ATTR_APP_LABEL, params.appLabel);
1566 writeUriAttribute(out, ATTR_ORIGINATING_URI, params.originatingUri);
1567 writeIntAttribute(out, ATTR_ORIGINATING_UID, params.originatingUid);
1568 writeUriAttribute(out, ATTR_REFERRER_URI, params.referrerUri);
1569 writeStringAttribute(out, ATTR_ABI_OVERRIDE, params.abiOverride);
1570 writeStringAttribute(out, ATTR_VOLUME_UUID, params.volumeUuid);
1571 writeIntAttribute(out, ATTR_INSTALL_REASON, params.installReason);
1572
1573 // Persist app icon if changed since last written
1574 File appIconFile = buildAppIconFile(sessionId, sessionsDir);
1575 if (params.appIcon == null && appIconFile.exists()) {
1576 appIconFile.delete();
1577 } else if (params.appIcon != null
1578 && appIconFile.lastModified() != params.appIconLastModified) {
1579 if (LOGD) Slog.w(TAG, "Writing changed icon " + appIconFile);
1580 FileOutputStream os = null;
1581 try {
1582 os = new FileOutputStream(appIconFile);
1583 params.appIcon.compress(Bitmap.CompressFormat.PNG, 90, os);
1584 } catch (IOException e) {
1585 Slog.w(TAG, "Failed to write icon " + appIconFile + ": " + e.getMessage());
1586 } finally {
1587 IoUtils.closeQuietly(os);
1588 }
1589
1590 params.appIconLastModified = appIconFile.lastModified();
1591 }
1592
1593 writeGrantedRuntimePermissionsLocked(out, params.grantedRuntimePermissions);
1594 }
1595
1596 out.endTag(null, TAG_SESSION);
1597 }
1598
1599 private static String[] readGrantedRuntimePermissions(XmlPullParser in)
1600 throws IOException, XmlPullParserException {
1601 List<String> permissions = null;
1602
1603 final int outerDepth = in.getDepth();
1604 int type;
1605 while ((type = in.next()) != XmlPullParser.END_DOCUMENT
1606 && (type != XmlPullParser.END_TAG || in.getDepth() > outerDepth)) {
1607 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1608 continue;
1609 }
1610 if (TAG_GRANTED_RUNTIME_PERMISSION.equals(in.getName())) {
1611 String permission = readStringAttribute(in, ATTR_NAME);
1612 if (permissions == null) {
1613 permissions = new ArrayList<>();
1614 }
1615 permissions.add(permission);
1616 }
1617 }
1618
1619 if (permissions == null) {
1620 return null;
1621 }
1622
1623 String[] permissionsArray = new String[permissions.size()];
1624 permissions.toArray(permissionsArray);
1625 return permissionsArray;
1626 }
1627
1628 /**
1629 * Read new session from a {@link XmlPullParser xml description} and create it.
1630 *
1631 * @param in The source of the description
1632 * @param callback Callback the session uses to notify about changes of it's state
1633 * @param context Context to be used by the session
1634 * @param pm PackageManager to use by the session
1635 * @param installerThread Thread to be used for callbacks of this session
1636 * @param sessionsDir The directory the sessions are stored in
1637 *
1638 * @return The newly created session
1639 */
1640 public static PackageInstallerSession readFromXml(@NonNull XmlPullParser in,
1641 @NonNull PackageInstallerService.InternalCallback callback, @NonNull Context context,
1642 @NonNull PackageManagerService pm, Looper installerThread, @NonNull File sessionsDir)
1643 throws IOException, XmlPullParserException {
1644 final int sessionId = readIntAttribute(in, ATTR_SESSION_ID);
1645 final int userId = readIntAttribute(in, ATTR_USER_ID);
1646 final String installerPackageName = readStringAttribute(in, ATTR_INSTALLER_PACKAGE_NAME);
1647 final int installerUid = readIntAttribute(in, ATTR_INSTALLER_UID, pm.getPackageUid(
1648 installerPackageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId));
1649 final long createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS);
1650 final String stageDirRaw = readStringAttribute(in, ATTR_SESSION_STAGE_DIR);
1651 final File stageDir = (stageDirRaw != null) ? new File(stageDirRaw) : null;
1652 final String stageCid = readStringAttribute(in, ATTR_SESSION_STAGE_CID);
1653 final boolean prepared = readBooleanAttribute(in, ATTR_PREPARED, true);
1654 final boolean sealed = readBooleanAttribute(in, ATTR_SEALED);
1655
1656 final SessionParams params = new SessionParams(
1657 SessionParams.MODE_INVALID);
1658 params.mode = readIntAttribute(in, ATTR_MODE);
1659 params.installFlags = readIntAttribute(in, ATTR_INSTALL_FLAGS);
1660 params.installLocation = readIntAttribute(in, ATTR_INSTALL_LOCATION);
1661 params.sizeBytes = readLongAttribute(in, ATTR_SIZE_BYTES);
1662 params.appPackageName = readStringAttribute(in, ATTR_APP_PACKAGE_NAME);
1663 params.appIcon = readBitmapAttribute(in, ATTR_APP_ICON);
1664 params.appLabel = readStringAttribute(in, ATTR_APP_LABEL);
1665 params.originatingUri = readUriAttribute(in, ATTR_ORIGINATING_URI);
1666 params.originatingUid =
1667 readIntAttribute(in, ATTR_ORIGINATING_UID, SessionParams.UID_UNKNOWN);
1668 params.referrerUri = readUriAttribute(in, ATTR_REFERRER_URI);
1669 params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE);
1670 params.volumeUuid = readStringAttribute(in, ATTR_VOLUME_UUID);
1671 params.grantedRuntimePermissions = readGrantedRuntimePermissions(in);
1672 params.installReason = readIntAttribute(in, ATTR_INSTALL_REASON);
1673
1674 final File appIconFile = buildAppIconFile(sessionId, sessionsDir);
1675 if (appIconFile.exists()) {
1676 params.appIcon = BitmapFactory.decodeFile(appIconFile.getAbsolutePath());
1677 params.appIconLastModified = appIconFile.lastModified();
1678 }
1679
1680 return new PackageInstallerSession(callback, context, pm,
1681 installerThread, sessionId, userId, installerPackageName, installerUid,
1682 params, createdMillis, stageDir, stageCid, prepared, sealed);
1683 }
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -07001684}