blob: 1fa37b91b8e8464531536eb618f5f81df664d20f [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 Sharkey1cb2d0d2014-07-30 16:45:01 -070019import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
20import static org.xmlpull.v1.XmlPullParser.START_TAG;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070021
Svetoslav805b63e2015-04-09 17:28:54 -070022import android.Manifest;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070023import android.app.ActivityManager;
Benjamin Franz39fb7fd2015-02-18 16:11:18 +000024import android.app.AppGlobals;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070025import android.app.AppOpsManager;
Benjamin Franz39fb7fd2015-02-18 16:11:18 +000026import android.app.Notification;
27import android.app.NotificationManager;
Jeff Sharkeya0907432014-08-15 10:23:11 -070028import android.app.PackageDeleteObserver;
29import android.app.PackageInstallObserver;
Benjamin Franz39fb7fd2015-02-18 16:11:18 +000030import android.app.admin.DevicePolicyManager;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070031import android.content.Context;
Jeff Sharkeyf0600952014-08-07 17:31:53 -070032import android.content.Intent;
Jeff Sharkeya0907432014-08-15 10:23:11 -070033import android.content.IntentSender;
34import android.content.IntentSender.SendIntentException;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070035import android.content.pm.IPackageInstaller;
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -070036import android.content.pm.IPackageInstallerCallback;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070037import android.content.pm.IPackageInstallerSession;
Benjamin Franz39fb7fd2015-02-18 16:11:18 +000038import android.content.pm.PackageInfo;
Jeff Sharkeyf0600952014-08-07 17:31:53 -070039import android.content.pm.PackageInstaller;
Jeff Sharkeya0907432014-08-15 10:23:11 -070040import android.content.pm.PackageInstaller.SessionInfo;
41import android.content.pm.PackageInstaller.SessionParams;
Jeff Sharkeyec9bad22014-09-05 09:45:20 -070042import android.content.pm.PackageManager;
Jeff Sharkey97d47ed2014-10-15 09:19:47 -070043import android.content.pm.ParceledListSlice;
Svet Ganov67882122016-12-11 16:36:34 -080044import android.content.pm.VersionedPackage;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070045import android.graphics.Bitmap;
Jeff Sharkeyf0600952014-08-07 17:31:53 -070046import android.net.Uri;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070047import android.os.Binder;
Jeff Sharkeya0907432014-08-15 10:23:11 -070048import android.os.Bundle;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070049import android.os.Environment;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070050import android.os.Handler;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070051import android.os.HandlerThread;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070052import android.os.Looper;
53import android.os.Message;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070054import android.os.Process;
Jeff Sharkeya1031142014-07-12 18:09:46 -070055import android.os.RemoteCallbackList;
56import android.os.RemoteException;
Jeff Sharkeyec55ef02014-07-08 11:28:00 -070057import android.os.SELinux;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070058import android.os.UserHandle;
59import android.os.UserManager;
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -070060import android.os.storage.StorageManager;
Jeff Sharkeyec55ef02014-07-08 11:28:00 -070061import android.system.ErrnoException;
62import android.system.Os;
Jeff Sharkeya0907432014-08-15 10:23:11 -070063import android.text.TextUtils;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070064import android.text.format.DateUtils;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070065import android.util.ArraySet;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070066import android.util.AtomicFile;
Jeff Sharkeya1031142014-07-12 18:09:46 -070067import android.util.ExceptionUtils;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070068import android.util.Slog;
69import android.util.SparseArray;
Jeff Sharkey742e7902014-08-16 19:09:13 -070070import android.util.SparseBooleanArray;
Narayan Kamatha22a7662017-06-12 13:34:29 +010071import android.util.SparseIntArray;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070072import android.util.Xml;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070073
Benjamin Franz39fb7fd2015-02-18 16:11:18 +000074import com.android.internal.R;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070075import com.android.internal.annotations.GuardedBy;
Jeff Sharkey742e7902014-08-16 19:09:13 -070076import com.android.internal.content.PackageHelper;
Chris Wren282cfef2017-03-27 15:01:44 -040077import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
Geoffrey Pitschaf759c52017-02-15 09:35:38 -050078import com.android.internal.notification.SystemNotificationChannels;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070079import com.android.internal.util.FastXmlSerializer;
Benjamin Franz39fb7fd2015-02-18 16:11:18 +000080import com.android.internal.util.ImageUtils;
Jeff Sharkeya1031142014-07-12 18:09:46 -070081import com.android.internal.util.IndentingPrintWriter;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070082import com.android.server.IoThread;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070083
Philip P. Moltmann7460c592017-08-08 20:07:11 +000084import libcore.io.IoUtils;
85
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070086import org.xmlpull.v1.XmlPullParser;
87import org.xmlpull.v1.XmlPullParserException;
88import org.xmlpull.v1.XmlSerializer;
89
Philip P. Moltmann7460c592017-08-08 20:07:11 +000090import java.io.CharArrayWriter;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070091import java.io.File;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070092import java.io.FileInputStream;
93import java.io.FileNotFoundException;
94import java.io.FileOutputStream;
Jeff Sharkeyec55ef02014-07-08 11:28:00 -070095import java.io.FilenameFilter;
96import java.io.IOException;
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +010097import java.nio.charset.StandardCharsets;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070098import java.security.SecureRandom;
Jeff Sharkeybb580672014-07-10 12:10:25 -070099import java.util.ArrayList;
Jeff Sharkey54d42be2015-07-20 16:36:55 -0700100import java.util.Collections;
Jeff Sharkeybb580672014-07-10 12:10:25 -0700101import java.util.List;
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700102import java.util.Objects;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700103import java.util.Random;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700104
105public class PackageInstallerService extends IPackageInstaller.Stub {
106 private static final String TAG = "PackageInstaller";
Jeff Sharkeye9808042014-09-11 21:15:37 -0700107 private static final boolean LOGD = false;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700108
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700109 // TODO: remove outstanding sessions when installer package goes away
Jeff Sharkey6c833e02014-07-14 22:44:30 -0700110 // TODO: notify listeners in other users when package has been installed there
Jeff Sharkey742e7902014-08-16 19:09:13 -0700111 // TODO: purge expired sessions periodically in addition to at reboot
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700112
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700113 /** XML constants used in {@link #mSessionsFile} */
114 private static final String TAG_SESSIONS = "sessions";
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700115
Jeff Sharkeyf174c6e2014-08-05 10:42:27 -0700116 /** Automatically destroy sessions older than this */
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700117 private static final long MAX_AGE_MILLIS = 3 * DateUtils.DAY_IN_MILLIS;
Jeff Sharkeyf174c6e2014-08-05 10:42:27 -0700118 /** Upper bound on number of active sessions for a UID */
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700119 private static final long MAX_ACTIVE_SESSIONS = 1024;
Jeff Sharkeyf174c6e2014-08-05 10:42:27 -0700120 /** Upper bound on number of historical sessions for a UID */
121 private static final long MAX_HISTORICAL_SESSIONS = 1048576;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700122
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700123 private final Context mContext;
124 private final PackageManagerService mPm;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700125
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -0700126 private AppOpsManager mAppOps;
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -0700127
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700128 private final HandlerThread mInstallThread;
Jeff Sharkeycbf47912014-09-12 09:55:32 -0700129 private final Handler mInstallHandler;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700130
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700131 private final Callbacks mCallbacks;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700132
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700133 /**
Jeff Sharkey02bd7842014-10-06 15:14:27 -0700134 * File storing persisted {@link #mSessions} metadata.
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700135 */
136 private final AtomicFile mSessionsFile;
137
Jeff Sharkey02bd7842014-10-06 15:14:27 -0700138 /**
139 * Directory storing persisted {@link #mSessions} metadata which is too
140 * heavy to store directly in {@link #mSessionsFile}.
141 */
142 private final File mSessionsDir;
143
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700144 private final InternalCallback mInternalCallback = new InternalCallback();
145
146 /**
147 * Used for generating session IDs. Since this is created at boot time,
148 * normal random might be predictable.
149 */
150 private final Random mRandom = new SecureRandom();
151
Todd Kennedy28c4e802016-07-13 13:20:30 -0700152 /** All sessions allocated */
153 @GuardedBy("mSessions")
154 private final SparseBooleanArray mAllocatedSessions = new SparseBooleanArray();
155
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700156 @GuardedBy("mSessions")
157 private final SparseArray<PackageInstallerSession> mSessions = new SparseArray<>();
158
Jeff Sharkey9a445772014-07-16 11:32:08 -0700159 /** Historical sessions kept around for debugging purposes */
160 @GuardedBy("mSessions")
Narayan Kamatha22a7662017-06-12 13:34:29 +0100161 private final List<String> mHistoricalSessions = new ArrayList<>();
162
163 @GuardedBy("mSessions")
164 private final SparseIntArray mHistoricalSessionsByInstaller = new SparseIntArray();
Jeff Sharkey9a445772014-07-16 11:32:08 -0700165
Jeff Sharkey742e7902014-08-16 19:09:13 -0700166 /** Sessions allocated to legacy users */
167 @GuardedBy("mSessions")
168 private final SparseBooleanArray mLegacySessions = new SparseBooleanArray();
169
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700170 private static final FilenameFilter sStageFilter = new FilenameFilter() {
171 @Override
172 public boolean accept(File dir, String name) {
Jeff Sharkey742e7902014-08-16 19:09:13 -0700173 return isStageName(name);
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700174 }
175 };
176
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -0700177 public PackageInstallerService(Context context, PackageManagerService pm) {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700178 mContext = context;
179 mPm = pm;
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700180
181 mInstallThread = new HandlerThread(TAG);
182 mInstallThread.start();
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700183
Jeff Sharkeycbf47912014-09-12 09:55:32 -0700184 mInstallHandler = new Handler(mInstallThread.getLooper());
185
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700186 mCallbacks = new Callbacks(mInstallThread.getLooper());
187
188 mSessionsFile = new AtomicFile(
Jeff Sharkey8212ae02016-02-10 14:46:43 -0700189 new File(Environment.getDataSystemDirectory(), "install_sessions.xml"));
190 mSessionsDir = new File(Environment.getDataSystemDirectory(), "install_sessions");
Jeff Sharkey02bd7842014-10-06 15:14:27 -0700191 mSessionsDir.mkdirs();
Tony Mak606f8e72017-02-15 18:40:03 +0000192 }
193
194 public void systemReady() {
195 mAppOps = mContext.getSystemService(AppOpsManager.class);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700196
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700197 synchronized (mSessions) {
198 readSessionsLocked();
199
Svetoslav Ganov096d3042017-01-30 16:34:13 -0800200 reconcileStagesLocked(StorageManager.UUID_PRIVATE_INTERNAL, false /*isInstant*/);
201 reconcileStagesLocked(StorageManager.UUID_PRIVATE_INTERNAL, true /*isInstant*/);
Jeff Sharkey6dce4962015-07-03 18:08:41 -0700202
Jeff Sharkey54d42be2015-07-20 16:36:55 -0700203 final ArraySet<File> unclaimedIcons = newArraySet(
Jeff Sharkey02bd7842014-10-06 15:14:27 -0700204 mSessionsDir.listFiles());
Jeff Sharkey742e7902014-08-16 19:09:13 -0700205
Jeff Sharkey02bd7842014-10-06 15:14:27 -0700206 // Ignore stages and icons claimed by active sessions
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700207 for (int i = 0; i < mSessions.size(); i++) {
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700208 final PackageInstallerSession session = mSessions.valueAt(i);
Jeff Sharkey02bd7842014-10-06 15:14:27 -0700209 unclaimedIcons.remove(buildAppIconFile(session.sessionId));
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700210 }
Jeff Sharkey742e7902014-08-16 19:09:13 -0700211
Jeff Sharkey02bd7842014-10-06 15:14:27 -0700212 // Clean up orphaned icons
213 for (File icon : unclaimedIcons) {
214 Slog.w(TAG, "Deleting orphan icon " + icon);
215 icon.delete();
216 }
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700217 }
218 }
219
Todd Kennedy2699f062015-11-20 13:07:17 -0800220 private void reconcileStagesLocked(String volumeUuid, boolean isEphemeral) {
221 final File stagingDir = buildStagingDir(volumeUuid, isEphemeral);
Jeff Sharkey54d42be2015-07-20 16:36:55 -0700222 final ArraySet<File> unclaimedStages = newArraySet(
Jeff Sharkey6dce4962015-07-03 18:08:41 -0700223 stagingDir.listFiles(sStageFilter));
224
225 // Ignore stages claimed by active sessions
226 for (int i = 0; i < mSessions.size(); i++) {
227 final PackageInstallerSession session = mSessions.valueAt(i);
228 unclaimedStages.remove(session.stageDir);
229 }
230
231 // Clean up orphaned staging directories
232 for (File stage : unclaimedStages) {
233 Slog.w(TAG, "Deleting orphan stage " + stage);
234 synchronized (mPm.mInstallLock) {
Jeff Sharkeyfdeeeea2016-01-11 17:34:24 -0700235 mPm.removeCodePathLI(stage);
Jeff Sharkey6dce4962015-07-03 18:08:41 -0700236 }
237 }
238 }
239
240 public void onPrivateVolumeMounted(String volumeUuid) {
241 synchronized (mSessions) {
Svetoslav Ganov096d3042017-01-30 16:34:13 -0800242 reconcileStagesLocked(volumeUuid, false /*isInstant*/);
Jeff Sharkey6dce4962015-07-03 18:08:41 -0700243 }
244 }
245
Jeff Sharkey742e7902014-08-16 19:09:13 -0700246 public void onSecureContainersAvailable() {
247 synchronized (mSessions) {
248 final ArraySet<String> unclaimed = new ArraySet<>();
249 for (String cid : PackageHelper.getSecureContainerList()) {
250 if (isStageName(cid)) {
251 unclaimed.add(cid);
252 }
253 }
254
255 // Ignore stages claimed by active sessions
256 for (int i = 0; i < mSessions.size(); i++) {
257 final PackageInstallerSession session = mSessions.valueAt(i);
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700258 final String cid = session.stageCid;
Jeff Sharkey742e7902014-08-16 19:09:13 -0700259
260 if (unclaimed.remove(cid)) {
261 // Claimed by active session, mount it
262 PackageHelper.mountSdDir(cid, PackageManagerService.getEncryptKey(),
263 Process.SYSTEM_UID);
264 }
265 }
266
267 // Clean up orphaned staging containers
268 for (String cid : unclaimed) {
269 Slog.w(TAG, "Deleting orphan container " + cid);
270 PackageHelper.destroySdDir(cid);
271 }
272 }
273 }
274
275 public static boolean isStageName(String name) {
276 final boolean isFile = name.startsWith("vmdl") && name.endsWith(".tmp");
277 final boolean isContainer = name.startsWith("smdl") && name.endsWith(".tmp");
278 final boolean isLegacyContainer = name.startsWith("smdl2tmp");
279 return isFile || isContainer || isLegacyContainer;
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700280 }
281
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700282 @Deprecated
Todd Kennedy2699f062015-11-20 13:07:17 -0800283 public File allocateStageDirLegacy(String volumeUuid, boolean isEphemeral) throws IOException {
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700284 synchronized (mSessions) {
285 try {
286 final int sessionId = allocateSessionIdLocked();
Jeff Sharkey742e7902014-08-16 19:09:13 -0700287 mLegacySessions.put(sessionId, true);
Todd Kennedy2699f062015-11-20 13:07:17 -0800288 final File stageDir = buildStageDir(volumeUuid, sessionId, isEphemeral);
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -0700289 prepareStageDir(stageDir);
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700290 return stageDir;
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700291 } catch (IllegalStateException e) {
292 throw new IOException(e);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700293 }
294 }
295 }
296
Jeff Sharkey742e7902014-08-16 19:09:13 -0700297 @Deprecated
298 public String allocateExternalStageCidLegacy() {
299 synchronized (mSessions) {
300 final int sessionId = allocateSessionIdLocked();
301 mLegacySessions.put(sessionId, true);
302 return "smdl" + sessionId + ".tmp";
303 }
304 }
305
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700306 private void readSessionsLocked() {
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700307 if (LOGD) Slog.v(TAG, "readSessionsLocked()");
308
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700309 mSessions.clear();
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700310
311 FileInputStream fis = null;
312 try {
313 fis = mSessionsFile.openRead();
314 final XmlPullParser in = Xml.newPullParser();
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +0100315 in.setInput(fis, StandardCharsets.UTF_8.name());
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700316
317 int type;
318 while ((type = in.next()) != END_DOCUMENT) {
319 if (type == START_TAG) {
320 final String tag = in.getName();
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000321 if (PackageInstallerSession.TAG_SESSION.equals(tag)) {
Philip P. Moltmann37dd1ba2017-09-08 09:46:22 -0700322 final PackageInstallerSession session;
323 try {
324 session = PackageInstallerSession.readFromXml(in, mInternalCallback,
325 mContext, mPm, mInstallThread.getLooper(), mSessionsDir);
326 } catch (Exception e) {
327 Slog.e(TAG, "Could not read session", e);
328 continue;
329 }
330
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700331 final long age = System.currentTimeMillis() - session.createdMillis;
332
333 final boolean valid;
334 if (age >= MAX_AGE_MILLIS) {
335 Slog.w(TAG, "Abandoning old session first created at "
336 + session.createdMillis);
337 valid = false;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700338 } else {
339 valid = true;
340 }
341
342 if (valid) {
343 mSessions.put(session.sessionId, session);
344 } else {
345 // Since this is early during boot we don't send
346 // any observer events about the session, but we
347 // keep details around for dumpsys.
Narayan Kamatha22a7662017-06-12 13:34:29 +0100348 addHistoricalSessionLocked(session);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700349 }
Todd Kennedy28c4e802016-07-13 13:20:30 -0700350 mAllocatedSessions.put(session.sessionId, true);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700351 }
352 }
353 }
354 } catch (FileNotFoundException e) {
355 // Missing sessions are okay, probably first boot
Svet Ganov7121e182015-07-13 22:38:12 -0700356 } catch (IOException | XmlPullParserException e) {
Dianne Hackborn8d051722014-10-01 14:59:58 -0700357 Slog.wtf(TAG, "Failed reading install sessions", e);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700358 } finally {
359 IoUtils.closeQuietly(fis);
360 }
361 }
362
Narayan Kamatha22a7662017-06-12 13:34:29 +0100363 private void addHistoricalSessionLocked(PackageInstallerSession session) {
364 CharArrayWriter writer = new CharArrayWriter();
365 IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
366 session.dump(pw);
367 mHistoricalSessions.add(writer.toString());
368
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000369 int installerUid = session.getInstallerUid();
Narayan Kamatha22a7662017-06-12 13:34:29 +0100370 // Increment the number of sessions by this installerUid.
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000371 mHistoricalSessionsByInstaller.put(installerUid,
372 mHistoricalSessionsByInstaller.get(installerUid) + 1);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700373 }
374
375 private void writeSessionsLocked() {
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700376 if (LOGD) Slog.v(TAG, "writeSessionsLocked()");
377
378 FileOutputStream fos = null;
379 try {
380 fos = mSessionsFile.startWrite();
381
382 XmlSerializer out = new FastXmlSerializer();
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +0100383 out.setOutput(fos, StandardCharsets.UTF_8.name());
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700384 out.startDocument(null, true);
385 out.startTag(null, TAG_SESSIONS);
386 final int size = mSessions.size();
387 for (int i = 0; i < size; i++) {
388 final PackageInstallerSession session = mSessions.valueAt(i);
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000389 session.write(out, mSessionsDir);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700390 }
391 out.endTag(null, TAG_SESSIONS);
392 out.endDocument();
393
394 mSessionsFile.finishWrite(fos);
395 } catch (IOException e) {
396 if (fos != null) {
397 mSessionsFile.failWrite(fos);
398 }
399 }
400 }
401
Jeff Sharkey02bd7842014-10-06 15:14:27 -0700402 private File buildAppIconFile(int sessionId) {
403 return new File(mSessionsDir, "app_icon." + sessionId + ".png");
404 }
405
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700406 private void writeSessionsAsync() {
407 IoThread.getHandler().post(new Runnable() {
408 @Override
409 public void run() {
410 synchronized (mSessions) {
411 writeSessionsLocked();
412 }
413 }
414 });
415 }
416
417 @Override
Jeff Sharkeya0907432014-08-15 10:23:11 -0700418 public int createSession(SessionParams params, String installerPackageName, int userId) {
Jeff Sharkey742e7902014-08-16 19:09:13 -0700419 try {
420 return createSessionInternal(params, installerPackageName, userId);
421 } catch (IOException e) {
422 throw ExceptionUtils.wrap(e);
423 }
424 }
425
426 private int createSessionInternal(SessionParams params, String installerPackageName, int userId)
427 throws IOException {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700428 final int callingUid = Binder.getCallingUid();
Jeff Sharkeye9808042014-09-11 21:15:37 -0700429 mPm.enforceCrossUserPermission(callingUid, userId, true, true, "createSession");
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700430
Jeff Sharkeye9808042014-09-11 21:15:37 -0700431 if (mPm.isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700432 throw new SecurityException("User restriction prevents installing");
433 }
434
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700435 if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {
Jeff Sharkeye9808042014-09-11 21:15:37 -0700436 params.installFlags |= PackageManager.INSTALL_FROM_ADB;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700437
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700438 } else {
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700439 mAppOps.checkPackage(callingUid, installerPackageName);
440
Jeff Sharkeye9808042014-09-11 21:15:37 -0700441 params.installFlags &= ~PackageManager.INSTALL_FROM_ADB;
442 params.installFlags &= ~PackageManager.INSTALL_ALL_USERS;
443 params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
Todd Kennedy78a72502017-07-19 12:49:30 -0700444 if ((params.installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0
445 && !mPm.isCallerVerifier(callingUid)) {
446 params.installFlags &= ~PackageManager.INSTALL_VIRTUAL_PRELOAD;
447 }
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700448 }
449
Svetoslav805b63e2015-04-09 17:28:54 -0700450 // Only system components can circumvent runtime permissions when installing.
451 if ((params.installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0
452 && mContext.checkCallingOrSelfPermission(Manifest.permission
453 .INSTALL_GRANT_RUNTIME_PERMISSIONS) == PackageManager.PERMISSION_DENIED) {
454 throw new SecurityException("You need the "
455 + "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission "
456 + "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag");
457 }
458
Jeff Sharkey8c61e392017-02-24 09:22:48 -0700459 if ((params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0
460 || (params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
461 throw new IllegalArgumentException(
462 "New installs into ASEC containers no longer supported");
463 }
464
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700465 // Defensively resize giant app icons
466 if (params.appIcon != null) {
467 final ActivityManager am = (ActivityManager) mContext.getSystemService(
468 Context.ACTIVITY_SERVICE);
469 final int iconSize = am.getLauncherLargeIconSize();
470 if ((params.appIcon.getWidth() > iconSize * 2)
471 || (params.appIcon.getHeight() > iconSize * 2)) {
472 params.appIcon = Bitmap.createScaledBitmap(params.appIcon, iconSize, iconSize,
473 true);
474 }
475 }
476
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -0700477 switch (params.mode) {
478 case SessionParams.MODE_FULL_INSTALL:
479 case SessionParams.MODE_INHERIT_EXISTING:
480 break;
481 default:
482 throw new IllegalArgumentException("Invalid install mode: " + params.mode);
483 }
484
485 // If caller requested explicit location, sanity check it, otherwise
486 // resolve the best internal or adopted location.
487 if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
Jeff Sharkey683bcd32017-03-18 17:54:51 -0600488 if (!PackageHelper.fitsOnInternal(mContext, params)) {
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -0700489 throw new IOException("No suitable internal storage available");
490 }
491
492 } else if ((params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
Jeff Sharkey683bcd32017-03-18 17:54:51 -0600493 if (!PackageHelper.fitsOnExternal(mContext, params)) {
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -0700494 throw new IOException("No suitable external storage available");
495 }
496
Jeff Sharkeyab234092015-06-09 21:42:22 -0700497 } else if ((params.installFlags & PackageManager.INSTALL_FORCE_VOLUME_UUID) != 0) {
498 // For now, installs to adopted media are treated as internal from
499 // an install flag point-of-view.
500 params.setInstallFlagsInternal();
501
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -0700502 } else {
503 // For now, installs to adopted media are treated as internal from
504 // an install flag point-of-view.
505 params.setInstallFlagsInternal();
506
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700507 // Resolve best location for install, based on combination of
508 // requested install flags, delta size, and manifest settings.
Robin Leee812d902014-08-21 16:51:18 +0100509 final long ident = Binder.clearCallingIdentity();
510 try {
Jeff Sharkey683bcd32017-03-18 17:54:51 -0600511 params.volumeUuid = PackageHelper.resolveInstallVolume(mContext, params);
Robin Leee812d902014-08-21 16:51:18 +0100512 } finally {
513 Binder.restoreCallingIdentity(ident);
514 }
Jeff Sharkeya1031142014-07-12 18:09:46 -0700515 }
516
517 final int sessionId;
518 final PackageInstallerSession session;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700519 synchronized (mSessions) {
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700520 // Sanity check that installer isn't going crazy
Jeff Sharkeyf174c6e2014-08-05 10:42:27 -0700521 final int activeCount = getSessionCount(mSessions, callingUid);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700522 if (activeCount >= MAX_ACTIVE_SESSIONS) {
Jeff Sharkeyf174c6e2014-08-05 10:42:27 -0700523 throw new IllegalStateException(
524 "Too many active sessions for UID " + callingUid);
525 }
Narayan Kamatha22a7662017-06-12 13:34:29 +0100526 final int historicalCount = mHistoricalSessionsByInstaller.get(callingUid);
Jeff Sharkeyf174c6e2014-08-05 10:42:27 -0700527 if (historicalCount >= MAX_HISTORICAL_SESSIONS) {
528 throw new IllegalStateException(
529 "Too many historical sessions for UID " + callingUid);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700530 }
531
Jeff Sharkeya1031142014-07-12 18:09:46 -0700532 sessionId = allocateSessionIdLocked();
Todd Kennedy04918fe2016-07-12 14:07:40 -0700533 }
Jeff Sharkeya1031142014-07-12 18:09:46 -0700534
Todd Kennedy04918fe2016-07-12 14:07:40 -0700535 final long createdMillis = System.currentTimeMillis();
536 // We're staging to exactly one location
537 File stageDir = null;
538 String stageCid = null;
539 if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
Todd Kennedybe0b8892017-02-15 14:13:52 -0800540 final boolean isInstant =
541 (params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
542 stageDir = buildStageDir(params.volumeUuid, sessionId, isInstant);
Todd Kennedy04918fe2016-07-12 14:07:40 -0700543 } else {
544 stageCid = buildExternalStageCid(sessionId);
545 }
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700546
Todd Kennedy04918fe2016-07-12 14:07:40 -0700547 session = new PackageInstallerSession(mInternalCallback, mContext, mPm,
548 mInstallThread.getLooper(), sessionId, userId, installerPackageName, callingUid,
549 params, createdMillis, stageDir, stageCid, false, false);
550
551 synchronized (mSessions) {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700552 mSessions.put(sessionId, session);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700553 }
Jeff Sharkeya1031142014-07-12 18:09:46 -0700554
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700555 mCallbacks.notifySessionCreated(session.sessionId, session.userId);
Jeff Sharkeya1031142014-07-12 18:09:46 -0700556 writeSessionsAsync();
557 return sessionId;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700558 }
559
Jeff Sharkey381d94b2014-08-24 14:45:56 -0700560 @Override
Jeff Sharkeyec9bad22014-09-05 09:45:20 -0700561 public void updateSessionAppIcon(int sessionId, Bitmap appIcon) {
562 synchronized (mSessions) {
563 final PackageInstallerSession session = mSessions.get(sessionId);
564 if (session == null || !isCallingUidOwner(session)) {
565 throw new SecurityException("Caller has no access to session " + sessionId);
566 }
Jeff Sharkey02bd7842014-10-06 15:14:27 -0700567
568 // Defensively resize giant app icons
569 if (appIcon != null) {
570 final ActivityManager am = (ActivityManager) mContext.getSystemService(
571 Context.ACTIVITY_SERVICE);
572 final int iconSize = am.getLauncherLargeIconSize();
573 if ((appIcon.getWidth() > iconSize * 2)
574 || (appIcon.getHeight() > iconSize * 2)) {
575 appIcon = Bitmap.createScaledBitmap(appIcon, iconSize, iconSize, true);
576 }
577 }
578
Jeff Sharkeyec9bad22014-09-05 09:45:20 -0700579 session.params.appIcon = appIcon;
Jeff Sharkey02bd7842014-10-06 15:14:27 -0700580 session.params.appIconLastModified = -1;
581
Jeff Sharkeyec9bad22014-09-05 09:45:20 -0700582 mInternalCallback.onSessionBadgingChanged(session);
583 }
584 }
585
586 @Override
587 public void updateSessionAppLabel(int sessionId, String appLabel) {
588 synchronized (mSessions) {
589 final PackageInstallerSession session = mSessions.get(sessionId);
590 if (session == null || !isCallingUidOwner(session)) {
591 throw new SecurityException("Caller has no access to session " + sessionId);
592 }
593 session.params.appLabel = appLabel;
594 mInternalCallback.onSessionBadgingChanged(session);
595 }
596 }
597
598 @Override
Jeff Sharkey381d94b2014-08-24 14:45:56 -0700599 public void abandonSession(int sessionId) {
600 synchronized (mSessions) {
601 final PackageInstallerSession session = mSessions.get(sessionId);
602 if (session == null || !isCallingUidOwner(session)) {
603 throw new SecurityException("Caller has no access to session " + sessionId);
604 }
605 session.abandon();
606 }
607 }
608
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700609 @Override
610 public IPackageInstallerSession openSession(int sessionId) {
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700611 try {
612 return openSessionInternal(sessionId);
613 } catch (IOException e) {
614 throw ExceptionUtils.wrap(e);
615 }
616 }
617
618 private IPackageInstallerSession openSessionInternal(int sessionId) throws IOException {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700619 synchronized (mSessions) {
620 final PackageInstallerSession session = mSessions.get(sessionId);
Jeff Sharkey381d94b2014-08-24 14:45:56 -0700621 if (session == null || !isCallingUidOwner(session)) {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700622 throw new SecurityException("Caller has no access to session " + sessionId);
623 }
Jeff Sharkey742e7902014-08-16 19:09:13 -0700624 session.open();
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700625 return session;
626 }
627 }
628
629 private int allocateSessionIdLocked() {
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700630 int n = 0;
631 int sessionId;
632 do {
Jeff Sharkeyf174c6e2014-08-05 10:42:27 -0700633 sessionId = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
Todd Kennedy28c4e802016-07-13 13:20:30 -0700634 if (!mAllocatedSessions.get(sessionId, false)) {
635 mAllocatedSessions.put(sessionId, true);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700636 return sessionId;
637 }
638 } while (n++ < 32);
639
640 throw new IllegalStateException("Failed to allocate session ID");
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700641 }
642
Todd Kennedy2699f062015-11-20 13:07:17 -0800643 private File buildStagingDir(String volumeUuid, boolean isEphemeral) {
Jeff Sharkey6dce4962015-07-03 18:08:41 -0700644 return Environment.getDataAppDirectory(volumeUuid);
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700645 }
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700646
Todd Kennedy2699f062015-11-20 13:07:17 -0800647 private File buildStageDir(String volumeUuid, int sessionId, boolean isEphemeral) {
648 final File stagingDir = buildStagingDir(volumeUuid, isEphemeral);
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -0700649 return new File(stagingDir, "vmdl" + sessionId + ".tmp");
650 }
651
652 static void prepareStageDir(File stageDir) throws IOException {
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700653 if (stageDir.exists()) {
654 throw new IOException("Session dir already exists: " + stageDir);
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700655 }
656
657 try {
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700658 Os.mkdir(stageDir.getAbsolutePath(), 0755);
659 Os.chmod(stageDir.getAbsolutePath(), 0755);
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700660 } catch (ErrnoException e) {
661 // This purposefully throws if directory already exists
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700662 throw new IOException("Failed to prepare session dir: " + stageDir, e);
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700663 }
664
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700665 if (!SELinux.restorecon(stageDir)) {
666 throw new IOException("Failed to restorecon session dir: " + stageDir);
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700667 }
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700668 }
669
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700670 private String buildExternalStageCid(int sessionId) {
671 return "smdl" + sessionId + ".tmp";
672 }
Jeff Sharkey742e7902014-08-16 19:09:13 -0700673
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700674 static void prepareExternalStageCid(String stageCid, long sizeBytes) throws IOException {
675 if (PackageHelper.createSdDir(sizeBytes, stageCid, PackageManagerService.getEncryptKey(),
Jeff Sharkey742e7902014-08-16 19:09:13 -0700676 Process.SYSTEM_UID, true) == null) {
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700677 throw new IOException("Failed to create session cid: " + stageCid);
Jeff Sharkey742e7902014-08-16 19:09:13 -0700678 }
Jeff Sharkey742e7902014-08-16 19:09:13 -0700679 }
680
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700681 @Override
Jeff Sharkeya0907432014-08-15 10:23:11 -0700682 public SessionInfo getSessionInfo(int sessionId) {
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700683 synchronized (mSessions) {
684 final PackageInstallerSession session = mSessions.get(sessionId);
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700685 return session != null ? session.generateInfo() : null;
686 }
687 }
688
689 @Override
Jeff Sharkey97d47ed2014-10-15 09:19:47 -0700690 public ParceledListSlice<SessionInfo> getAllSessions(int userId) {
Amith Yamasani8cd28b52014-06-08 17:54:27 -0700691 mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "getAllSessions");
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700692
Jeff Sharkeya0907432014-08-15 10:23:11 -0700693 final List<SessionInfo> result = new ArrayList<>();
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700694 synchronized (mSessions) {
695 for (int i = 0; i < mSessions.size(); i++) {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700696 final PackageInstallerSession session = mSessions.valueAt(i);
Jeff Sharkeybb580672014-07-10 12:10:25 -0700697 if (session.userId == userId) {
Jeff Sharkeyda1247a2017-06-08 14:13:29 -0600698 result.add(session.generateInfo(false));
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700699 }
700 }
701 }
Jeff Sharkey97d47ed2014-10-15 09:19:47 -0700702 return new ParceledListSlice<>(result);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700703 }
704
705 @Override
Jeff Sharkey97d47ed2014-10-15 09:19:47 -0700706 public ParceledListSlice<SessionInfo> getMySessions(String installerPackageName, int userId) {
Amith Yamasani8cd28b52014-06-08 17:54:27 -0700707 mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "getMySessions");
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700708 mAppOps.checkPackage(Binder.getCallingUid(), installerPackageName);
709
Jeff Sharkeya0907432014-08-15 10:23:11 -0700710 final List<SessionInfo> result = new ArrayList<>();
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700711 synchronized (mSessions) {
712 for (int i = 0; i < mSessions.size(); i++) {
713 final PackageInstallerSession session = mSessions.valueAt(i);
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000714
715 SessionInfo info = session.generateInfo(false);
716 if (Objects.equals(info.getInstallerPackageName(), installerPackageName)
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700717 && session.userId == userId) {
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000718 result.add(info);
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700719 }
720 }
721 }
Jeff Sharkey97d47ed2014-10-15 09:19:47 -0700722 return new ParceledListSlice<>(result);
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700723 }
724
725 @Override
Svet Ganov67882122016-12-11 16:36:34 -0800726 public void uninstall(VersionedPackage versionedPackage, String callerPackageName, int flags,
727 IntentSender statusReceiver, int userId) throws RemoteException {
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000728 final int callingUid = Binder.getCallingUid();
729 mPm.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall");
730 if ((callingUid != Process.SHELL_UID) && (callingUid != Process.ROOT_UID)) {
731 mAppOps.checkPackage(callingUid, callerPackageName);
732 }
733
Makoto Onukic8a5a552015-11-19 14:29:12 -0800734 // Check whether the caller is device owner, in which case we do it silently.
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000735 DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(
736 Context.DEVICE_POLICY_SERVICE);
Makoto Onukic8a5a552015-11-19 14:29:12 -0800737 boolean isDeviceOwner = (dpm != null) && dpm.isDeviceOwnerAppOnCallingUser(
738 callerPackageName);
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700739
Jeff Sharkeya0907432014-08-15 10:23:11 -0700740 final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext,
Svet Ganov67882122016-12-11 16:36:34 -0800741 statusReceiver, versionedPackage.getPackageName(), isDeviceOwner, userId);
Sudheer Shanka72de4dd2016-07-22 15:46:37 -0700742 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DELETE_PACKAGES)
743 == PackageManager.PERMISSION_GRANTED) {
Jeff Sharkeyf0600952014-08-07 17:31:53 -0700744 // Sweet, call straight through!
Svet Ganov67882122016-12-11 16:36:34 -0800745 mPm.deletePackageVersioned(versionedPackage, adapter.getBinder(), userId, flags);
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000746 } else if (isDeviceOwner) {
747 // Allow the DeviceOwner to silently delete packages
748 // Need to clear the calling identity to get DELETE_PACKAGES permission
749 long ident = Binder.clearCallingIdentity();
750 try {
Svet Ganov67882122016-12-11 16:36:34 -0800751 mPm.deletePackageVersioned(versionedPackage, adapter.getBinder(), userId, flags);
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000752 } finally {
753 Binder.restoreCallingIdentity(ident);
754 }
Jeff Sharkeyf0600952014-08-07 17:31:53 -0700755 } else {
756 // Take a short detour to confirm with user
757 final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
Svet Ganov67882122016-12-11 16:36:34 -0800758 intent.setData(Uri.fromParts("package", versionedPackage.getPackageName(), null));
Jeff Sharkeya0907432014-08-15 10:23:11 -0700759 intent.putExtra(PackageInstaller.EXTRA_CALLBACK, adapter.getBinder().asBinder());
760 adapter.onUserActionRequired(intent);
Jeff Sharkeyf0600952014-08-07 17:31:53 -0700761 }
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700762 }
763
764 @Override
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700765 public void setPermissionsResult(int sessionId, boolean accepted) {
766 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, TAG);
767
768 synchronized (mSessions) {
Svet Ganov3baa8762016-04-08 09:22:54 -0700769 PackageInstallerSession session = mSessions.get(sessionId);
770 if (session != null) {
771 session.setPermissionsResult(accepted);
772 }
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700773 }
774 }
775
776 @Override
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700777 public void registerCallback(IPackageInstallerCallback callback, int userId) {
Amith Yamasani8cd28b52014-06-08 17:54:27 -0700778 mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "registerCallback");
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700779 mCallbacks.register(callback, userId);
Jeff Sharkeybb580672014-07-10 12:10:25 -0700780 }
781
782 @Override
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700783 public void unregisterCallback(IPackageInstallerCallback callback) {
784 mCallbacks.unregister(callback);
Jeff Sharkeya1031142014-07-12 18:09:46 -0700785 }
786
Jeff Sharkeyf174c6e2014-08-05 10:42:27 -0700787 private static int getSessionCount(SparseArray<PackageInstallerSession> sessions,
788 int installerUid) {
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700789 int count = 0;
Jeff Sharkeyf174c6e2014-08-05 10:42:27 -0700790 final int size = sessions.size();
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700791 for (int i = 0; i < size; i++) {
Jeff Sharkeyf174c6e2014-08-05 10:42:27 -0700792 final PackageInstallerSession session = sessions.valueAt(i);
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000793 if (session.getInstallerUid() == installerUid) {
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700794 count++;
795 }
796 }
797 return count;
798 }
799
800 private boolean isCallingUidOwner(PackageInstallerSession session) {
801 final int callingUid = Binder.getCallingUid();
802 if (callingUid == Process.ROOT_UID) {
803 return true;
804 } else {
Philip P. Moltmann7460c592017-08-08 20:07:11 +0000805 return (session != null) && (callingUid == session.getInstallerUid());
Jeff Sharkeya1031142014-07-12 18:09:46 -0700806 }
807 }
808
Jeff Sharkeya0907432014-08-15 10:23:11 -0700809 static class PackageDeleteObserverAdapter extends PackageDeleteObserver {
810 private final Context mContext;
811 private final IntentSender mTarget;
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -0700812 private final String mPackageName;
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000813 private final Notification mNotification;
Jeff Sharkeya0907432014-08-15 10:23:11 -0700814
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -0700815 public PackageDeleteObserverAdapter(Context context, IntentSender target,
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000816 String packageName, boolean showNotification, int userId) {
Jeff Sharkeya0907432014-08-15 10:23:11 -0700817 mContext = context;
818 mTarget = target;
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -0700819 mPackageName = packageName;
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000820 if (showNotification) {
821 mNotification = buildSuccessNotification(mContext,
822 mContext.getResources().getString(R.string.package_deleted_device_owner),
823 packageName,
824 userId);
825 } else {
826 mNotification = null;
827 }
Jeff Sharkeya0907432014-08-15 10:23:11 -0700828 }
829
830 @Override
831 public void onUserActionRequired(Intent intent) {
832 final Intent fillIn = new Intent();
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -0700833 fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, mPackageName);
Jeff Sharkeya0907432014-08-15 10:23:11 -0700834 fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
Jeff Sharkey742e7902014-08-16 19:09:13 -0700835 PackageInstaller.STATUS_PENDING_USER_ACTION);
Jeff Sharkeya0907432014-08-15 10:23:11 -0700836 fillIn.putExtra(Intent.EXTRA_INTENT, intent);
837 try {
838 mTarget.sendIntent(mContext, 0, fillIn, null, null);
839 } catch (SendIntentException ignored) {
840 }
841 }
842
843 @Override
844 public void onPackageDeleted(String basePackageName, int returnCode, String msg) {
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000845 if (PackageManager.DELETE_SUCCEEDED == returnCode && mNotification != null) {
846 NotificationManager notificationManager = (NotificationManager)
847 mContext.getSystemService(Context.NOTIFICATION_SERVICE);
Chris Wren282cfef2017-03-27 15:01:44 -0400848 notificationManager.notify(basePackageName,
849 SystemMessage.NOTE_PACKAGE_STATE,
850 mNotification);
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000851 }
Jeff Sharkeya0907432014-08-15 10:23:11 -0700852 final Intent fillIn = new Intent();
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -0700853 fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, mPackageName);
Jeff Sharkeya0907432014-08-15 10:23:11 -0700854 fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
855 PackageManager.deleteStatusToPublicStatus(returnCode));
856 fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
857 PackageManager.deleteStatusToString(returnCode, msg));
858 fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode);
859 try {
860 mTarget.sendIntent(mContext, 0, fillIn, null, null);
861 } catch (SendIntentException ignored) {
862 }
863 }
864 }
865
866 static class PackageInstallObserverAdapter extends PackageInstallObserver {
867 private final Context mContext;
868 private final IntentSender mTarget;
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -0700869 private final int mSessionId;
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000870 private final boolean mShowNotification;
871 private final int mUserId;
Jeff Sharkeya0907432014-08-15 10:23:11 -0700872
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000873 public PackageInstallObserverAdapter(Context context, IntentSender target, int sessionId,
874 boolean showNotification, int userId) {
Jeff Sharkeya0907432014-08-15 10:23:11 -0700875 mContext = context;
876 mTarget = target;
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -0700877 mSessionId = sessionId;
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000878 mShowNotification = showNotification;
879 mUserId = userId;
Jeff Sharkeya0907432014-08-15 10:23:11 -0700880 }
881
882 @Override
883 public void onUserActionRequired(Intent intent) {
884 final Intent fillIn = new Intent();
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -0700885 fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId);
Jeff Sharkeya0907432014-08-15 10:23:11 -0700886 fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
Jeff Sharkey742e7902014-08-16 19:09:13 -0700887 PackageInstaller.STATUS_PENDING_USER_ACTION);
Jeff Sharkeya0907432014-08-15 10:23:11 -0700888 fillIn.putExtra(Intent.EXTRA_INTENT, intent);
889 try {
890 mTarget.sendIntent(mContext, 0, fillIn, null, null);
891 } catch (SendIntentException ignored) {
892 }
893 }
894
895 @Override
896 public void onPackageInstalled(String basePackageName, int returnCode, String msg,
897 Bundle extras) {
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000898 if (PackageManager.INSTALL_SUCCEEDED == returnCode && mShowNotification) {
Benjamin Franz2e3e9432015-04-17 15:28:17 +0100899 boolean update = (extras != null) && extras.getBoolean(Intent.EXTRA_REPLACING);
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000900 Notification notification = buildSuccessNotification(mContext,
Benjamin Franz2e3e9432015-04-17 15:28:17 +0100901 mContext.getResources()
902 .getString(update ? R.string.package_updated_device_owner :
903 R.string.package_installed_device_owner),
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000904 basePackageName,
905 mUserId);
906 if (notification != null) {
907 NotificationManager notificationManager = (NotificationManager)
908 mContext.getSystemService(Context.NOTIFICATION_SERVICE);
Chris Wren282cfef2017-03-27 15:01:44 -0400909 notificationManager.notify(basePackageName,
910 SystemMessage.NOTE_PACKAGE_STATE,
911 notification);
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000912 }
913 }
Jeff Sharkeya0907432014-08-15 10:23:11 -0700914 final Intent fillIn = new Intent();
Benjamin Franz2e3e9432015-04-17 15:28:17 +0100915 fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, basePackageName);
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -0700916 fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId);
Jeff Sharkeya0907432014-08-15 10:23:11 -0700917 fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
918 PackageManager.installStatusToPublicStatus(returnCode));
919 fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
920 PackageManager.installStatusToString(returnCode, msg));
921 fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode);
922 if (extras != null) {
923 final String existing = extras.getString(
924 PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE);
925 if (!TextUtils.isEmpty(existing)) {
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700926 fillIn.putExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME, existing);
Jeff Sharkeya0907432014-08-15 10:23:11 -0700927 }
928 }
929 try {
930 mTarget.sendIntent(mContext, 0, fillIn, null, null);
931 } catch (SendIntentException ignored) {
932 }
933 }
934 }
935
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000936 /**
937 * Build a notification for package installation / deletion by device owners that is shown if
938 * the operation succeeds.
939 */
940 private static Notification buildSuccessNotification(Context context, String contentText,
941 String basePackageName, int userId) {
942 PackageInfo packageInfo = null;
943 try {
944 packageInfo = AppGlobals.getPackageManager().getPackageInfo(
Svet Ganova5c867c2017-05-15 01:17:05 -0700945 basePackageName, PackageManager.MATCH_STATIC_SHARED_LIBRARIES, userId);
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000946 } catch (RemoteException ignored) {
947 }
948 if (packageInfo == null || packageInfo.applicationInfo == null) {
949 Slog.w(TAG, "Notification not built for package: " + basePackageName);
950 return null;
951 }
952 PackageManager pm = context.getPackageManager();
953 Bitmap packageIcon = ImageUtils.buildScaledBitmap(
954 packageInfo.applicationInfo.loadIcon(pm),
955 context.getResources().getDimensionPixelSize(
956 android.R.dimen.notification_large_icon_width),
957 context.getResources().getDimensionPixelSize(
958 android.R.dimen.notification_large_icon_height));
959 CharSequence packageLabel = packageInfo.applicationInfo.loadLabel(pm);
Geoffrey Pitschaf759c52017-02-15 09:35:38 -0500960 return new Notification.Builder(context, SystemNotificationChannels.DEVICE_ADMIN)
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000961 .setSmallIcon(R.drawable.ic_check_circle_24px)
962 .setColor(context.getResources().getColor(
963 R.color.system_notification_accent_color))
964 .setContentTitle(packageLabel)
965 .setContentText(contentText)
Benjamin Franz2e3e9432015-04-17 15:28:17 +0100966 .setStyle(new Notification.BigTextStyle().bigText(contentText))
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000967 .setLargeIcon(packageIcon)
968 .build();
969 }
970
Jeff Sharkey54d42be2015-07-20 16:36:55 -0700971 public static <E> ArraySet<E> newArraySet(E... elements) {
972 final ArraySet<E> set = new ArraySet<E>();
973 if (elements != null) {
974 set.ensureCapacity(elements.length);
975 Collections.addAll(set, elements);
976 }
977 return set;
978 }
979
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700980 private static class Callbacks extends Handler {
981 private static final int MSG_SESSION_CREATED = 1;
Jeff Sharkeyec9bad22014-09-05 09:45:20 -0700982 private static final int MSG_SESSION_BADGING_CHANGED = 2;
Jeff Sharkeybc7bce32014-09-05 15:53:05 -0700983 private static final int MSG_SESSION_ACTIVE_CHANGED = 3;
Jeff Sharkeyec9bad22014-09-05 09:45:20 -0700984 private static final int MSG_SESSION_PROGRESS_CHANGED = 4;
Jeff Sharkeybc7bce32014-09-05 15:53:05 -0700985 private static final int MSG_SESSION_FINISHED = 5;
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700986
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700987 private final RemoteCallbackList<IPackageInstallerCallback>
988 mCallbacks = new RemoteCallbackList<>();
Jeff Sharkeya1031142014-07-12 18:09:46 -0700989
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700990 public Callbacks(Looper looper) {
991 super(looper);
992 }
993
994 public void register(IPackageInstallerCallback callback, int userId) {
995 mCallbacks.register(callback, new UserHandle(userId));
996 }
997
998 public void unregister(IPackageInstallerCallback callback) {
999 mCallbacks.unregister(callback);
1000 }
1001
1002 @Override
1003 public void handleMessage(Message msg) {
1004 final int userId = msg.arg2;
1005 final int n = mCallbacks.beginBroadcast();
1006 for (int i = 0; i < n; i++) {
1007 final IPackageInstallerCallback callback = mCallbacks.getBroadcastItem(i);
1008 final UserHandle user = (UserHandle) mCallbacks.getBroadcastCookie(i);
1009 // TODO: dispatch notifications for slave profiles
1010 if (userId == user.getIdentifier()) {
1011 try {
1012 invokeCallback(callback, msg);
1013 } catch (RemoteException ignored) {
1014 }
Jeff Sharkeya1031142014-07-12 18:09:46 -07001015 }
1016 }
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001017 mCallbacks.finishBroadcast();
Jeff Sharkeya1031142014-07-12 18:09:46 -07001018 }
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001019
1020 private void invokeCallback(IPackageInstallerCallback callback, Message msg)
1021 throws RemoteException {
1022 final int sessionId = msg.arg1;
1023 switch (msg.what) {
1024 case MSG_SESSION_CREATED:
1025 callback.onSessionCreated(sessionId);
1026 break;
Jeff Sharkeyec9bad22014-09-05 09:45:20 -07001027 case MSG_SESSION_BADGING_CHANGED:
1028 callback.onSessionBadgingChanged(sessionId);
1029 break;
Jeff Sharkeybc7bce32014-09-05 15:53:05 -07001030 case MSG_SESSION_ACTIVE_CHANGED:
1031 callback.onSessionActiveChanged(sessionId, (boolean) msg.obj);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001032 break;
1033 case MSG_SESSION_PROGRESS_CHANGED:
1034 callback.onSessionProgressChanged(sessionId, (float) msg.obj);
1035 break;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001036 case MSG_SESSION_FINISHED:
1037 callback.onSessionFinished(sessionId, (boolean) msg.obj);
1038 break;
1039 }
1040 }
1041
1042 private void notifySessionCreated(int sessionId, int userId) {
1043 obtainMessage(MSG_SESSION_CREATED, sessionId, userId).sendToTarget();
1044 }
1045
Jeff Sharkeyec9bad22014-09-05 09:45:20 -07001046 private void notifySessionBadgingChanged(int sessionId, int userId) {
1047 obtainMessage(MSG_SESSION_BADGING_CHANGED, sessionId, userId).sendToTarget();
1048 }
1049
Jeff Sharkeybc7bce32014-09-05 15:53:05 -07001050 private void notifySessionActiveChanged(int sessionId, int userId, boolean active) {
1051 obtainMessage(MSG_SESSION_ACTIVE_CHANGED, sessionId, userId, active).sendToTarget();
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001052 }
1053
1054 private void notifySessionProgressChanged(int sessionId, int userId, float progress) {
1055 obtainMessage(MSG_SESSION_PROGRESS_CHANGED, sessionId, userId, progress).sendToTarget();
1056 }
1057
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001058 public void notifySessionFinished(int sessionId, int userId, boolean success) {
1059 obtainMessage(MSG_SESSION_FINISHED, sessionId, userId, success).sendToTarget();
1060 }
Jeff Sharkeya1031142014-07-12 18:09:46 -07001061 }
1062
1063 void dump(IndentingPrintWriter pw) {
Jeff Sharkeya1031142014-07-12 18:09:46 -07001064 synchronized (mSessions) {
Jeff Sharkey9a445772014-07-16 11:32:08 -07001065 pw.println("Active install sessions:");
1066 pw.increaseIndent();
1067 int N = mSessions.size();
Jeff Sharkeya1031142014-07-12 18:09:46 -07001068 for (int i = 0; i < N; i++) {
1069 final PackageInstallerSession session = mSessions.valueAt(i);
1070 session.dump(pw);
1071 pw.println();
1072 }
Jeff Sharkey9a445772014-07-16 11:32:08 -07001073 pw.println();
1074 pw.decreaseIndent();
1075
1076 pw.println("Historical install sessions:");
1077 pw.increaseIndent();
1078 N = mHistoricalSessions.size();
1079 for (int i = 0; i < N; i++) {
Narayan Kamatha22a7662017-06-12 13:34:29 +01001080 pw.print(mHistoricalSessions.get(i));
Jeff Sharkey9a445772014-07-16 11:32:08 -07001081 pw.println();
1082 }
1083 pw.println();
1084 pw.decreaseIndent();
Jeff Sharkey742e7902014-08-16 19:09:13 -07001085
1086 pw.println("Legacy install sessions:");
1087 pw.increaseIndent();
1088 pw.println(mLegacySessions.toString());
1089 pw.decreaseIndent();
Jeff Sharkeya1031142014-07-12 18:09:46 -07001090 }
Jeff Sharkeybb580672014-07-10 12:10:25 -07001091 }
1092
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001093 class InternalCallback {
Jeff Sharkeyec9bad22014-09-05 09:45:20 -07001094 public void onSessionBadgingChanged(PackageInstallerSession session) {
1095 mCallbacks.notifySessionBadgingChanged(session.sessionId, session.userId);
1096 writeSessionsAsync();
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001097 }
1098
Jeff Sharkeybc7bce32014-09-05 15:53:05 -07001099 public void onSessionActiveChanged(PackageInstallerSession session, boolean active) {
1100 mCallbacks.notifySessionActiveChanged(session.sessionId, session.userId, active);
Jeff Sharkey742e7902014-08-16 19:09:13 -07001101 }
1102
Jeff Sharkeyec9bad22014-09-05 09:45:20 -07001103 public void onSessionProgressChanged(PackageInstallerSession session, float progress) {
1104 mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId, progress);
1105 }
1106
Jeff Sharkeycbf47912014-09-12 09:55:32 -07001107 public void onSessionFinished(final PackageInstallerSession session, boolean success) {
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001108 mCallbacks.notifySessionFinished(session.sessionId, session.userId, success);
Jeff Sharkeycbf47912014-09-12 09:55:32 -07001109
1110 mInstallHandler.post(new Runnable() {
1111 @Override
1112 public void run() {
1113 synchronized (mSessions) {
1114 mSessions.remove(session.sessionId);
Narayan Kamatha22a7662017-06-12 13:34:29 +01001115 addHistoricalSessionLocked(session);
Jeff Sharkey02bd7842014-10-06 15:14:27 -07001116
1117 final File appIconFile = buildAppIconFile(session.sessionId);
1118 if (appIconFile.exists()) {
1119 appIconFile.delete();
1120 }
1121
Jeff Sharkeycbf47912014-09-12 09:55:32 -07001122 writeSessionsLocked();
1123 }
1124 }
1125 });
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -07001126 }
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -07001127
Jeff Sharkey77d218e2014-09-06 12:20:37 -07001128 public void onSessionPrepared(PackageInstallerSession session) {
1129 // We prepared the destination to write into; we want to persist
1130 // this, but it's not critical enough to block for.
1131 writeSessionsAsync();
1132 }
1133
Jeff Sharkeycbf47912014-09-12 09:55:32 -07001134 public void onSessionSealedBlocking(PackageInstallerSession session) {
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -07001135 // It's very important that we block until we've recorded the
1136 // session as being sealed, since we never want to allow mutation
1137 // after sealing.
Jeff Sharkeycbf47912014-09-12 09:55:32 -07001138 synchronized (mSessions) {
1139 writeSessionsLocked();
1140 }
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -07001141 }
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -07001142 }
1143}