blob: d25abbf1ff8ac65efab5a0807716f89727b24e0d [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 com.android.internal.util.XmlUtils.readBitmapAttribute;
20import static com.android.internal.util.XmlUtils.readBooleanAttribute;
21import static com.android.internal.util.XmlUtils.readIntAttribute;
22import static com.android.internal.util.XmlUtils.readLongAttribute;
23import static com.android.internal.util.XmlUtils.readStringAttribute;
24import static com.android.internal.util.XmlUtils.readUriAttribute;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070025import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
26import static com.android.internal.util.XmlUtils.writeIntAttribute;
27import static com.android.internal.util.XmlUtils.writeLongAttribute;
28import static com.android.internal.util.XmlUtils.writeStringAttribute;
29import static com.android.internal.util.XmlUtils.writeUriAttribute;
30import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
31import static org.xmlpull.v1.XmlPullParser.START_TAG;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070032
Svetoslav805b63e2015-04-09 17:28:54 -070033import android.Manifest;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070034import android.app.ActivityManager;
Benjamin Franz39fb7fd2015-02-18 16:11:18 +000035import android.app.AppGlobals;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070036import android.app.AppOpsManager;
Benjamin Franz39fb7fd2015-02-18 16:11:18 +000037import android.app.Notification;
38import android.app.NotificationManager;
Jeff Sharkeya0907432014-08-15 10:23:11 -070039import android.app.PackageDeleteObserver;
40import android.app.PackageInstallObserver;
Benjamin Franz39fb7fd2015-02-18 16:11:18 +000041import android.app.admin.DevicePolicyManager;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070042import android.content.Context;
Jeff Sharkeyf0600952014-08-07 17:31:53 -070043import android.content.Intent;
Jeff Sharkeya0907432014-08-15 10:23:11 -070044import android.content.IntentSender;
45import android.content.IntentSender.SendIntentException;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070046import android.content.pm.IPackageInstaller;
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -070047import android.content.pm.IPackageInstallerCallback;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070048import android.content.pm.IPackageInstallerSession;
Benjamin Franz39fb7fd2015-02-18 16:11:18 +000049import android.content.pm.PackageInfo;
Jeff Sharkeyf0600952014-08-07 17:31:53 -070050import android.content.pm.PackageInstaller;
Jeff Sharkeya0907432014-08-15 10:23:11 -070051import android.content.pm.PackageInstaller.SessionInfo;
52import android.content.pm.PackageInstaller.SessionParams;
Jeff Sharkeyec9bad22014-09-05 09:45:20 -070053import android.content.pm.PackageManager;
Jeff Sharkey97d47ed2014-10-15 09:19:47 -070054import android.content.pm.ParceledListSlice;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070055import android.graphics.Bitmap;
Jeff Sharkey02bd7842014-10-06 15:14:27 -070056import android.graphics.Bitmap.CompressFormat;
57import android.graphics.BitmapFactory;
Jeff Sharkeyf0600952014-08-07 17:31:53 -070058import android.net.Uri;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070059import android.os.Binder;
Jeff Sharkeya0907432014-08-15 10:23:11 -070060import android.os.Bundle;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070061import android.os.Environment;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070062import android.os.Handler;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070063import android.os.HandlerThread;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070064import android.os.Looper;
65import android.os.Message;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070066import android.os.Process;
Jeff Sharkeya1031142014-07-12 18:09:46 -070067import android.os.RemoteCallbackList;
68import android.os.RemoteException;
Jeff Sharkeyec55ef02014-07-08 11:28:00 -070069import android.os.SELinux;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070070import android.os.UserHandle;
71import android.os.UserManager;
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -070072import android.os.storage.StorageManager;
Jeff Sharkeyec55ef02014-07-08 11:28:00 -070073import android.system.ErrnoException;
74import android.system.Os;
Jeff Sharkeya0907432014-08-15 10:23:11 -070075import android.text.TextUtils;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070076import android.text.format.DateUtils;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070077import android.util.ArraySet;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070078import android.util.AtomicFile;
Jeff Sharkeya1031142014-07-12 18:09:46 -070079import android.util.ExceptionUtils;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070080import android.util.Slog;
81import android.util.SparseArray;
Jeff Sharkey742e7902014-08-16 19:09:13 -070082import android.util.SparseBooleanArray;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070083import android.util.Xml;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070084
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -070085import libcore.io.IoUtils;
86
Benjamin Franz39fb7fd2015-02-18 16:11:18 +000087import com.android.internal.R;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070088import com.android.internal.annotations.GuardedBy;
Jeff Sharkey742e7902014-08-16 19:09:13 -070089import com.android.internal.content.PackageHelper;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070090import com.android.internal.util.FastXmlSerializer;
Benjamin Franz39fb7fd2015-02-18 16:11:18 +000091import com.android.internal.util.ImageUtils;
Jeff Sharkeya1031142014-07-12 18:09:46 -070092import com.android.internal.util.IndentingPrintWriter;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070093import com.android.server.IoThread;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070094
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070095import org.xmlpull.v1.XmlPullParser;
96import org.xmlpull.v1.XmlPullParserException;
97import org.xmlpull.v1.XmlSerializer;
98
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070099import java.io.File;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700100import java.io.FileInputStream;
101import java.io.FileNotFoundException;
102import java.io.FileOutputStream;
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700103import java.io.FilenameFilter;
104import java.io.IOException;
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +0100105import java.nio.charset.StandardCharsets;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700106import java.security.SecureRandom;
Jeff Sharkeybb580672014-07-10 12:10:25 -0700107import java.util.ArrayList;
Jeff Sharkey54d42be2015-07-20 16:36:55 -0700108import java.util.Collections;
Jeff Sharkeybb580672014-07-10 12:10:25 -0700109import java.util.List;
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700110import java.util.Objects;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700111import java.util.Random;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700112
113public class PackageInstallerService extends IPackageInstaller.Stub {
114 private static final String TAG = "PackageInstaller";
Jeff Sharkeye9808042014-09-11 21:15:37 -0700115 private static final boolean LOGD = false;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700116
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700117 // TODO: remove outstanding sessions when installer package goes away
Jeff Sharkey6c833e02014-07-14 22:44:30 -0700118 // TODO: notify listeners in other users when package has been installed there
Jeff Sharkey742e7902014-08-16 19:09:13 -0700119 // TODO: purge expired sessions periodically in addition to at reboot
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700120
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700121 /** XML constants used in {@link #mSessionsFile} */
122 private static final String TAG_SESSIONS = "sessions";
123 private static final String TAG_SESSION = "session";
Svet Ganov7121e182015-07-13 22:38:12 -0700124 private static final String TAG_GRANTED_RUNTIME_PERMISSION = "granted-runtime-permission";
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700125 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";
Jeff Sharkeye9808042014-09-11 21:15:37 -0700128 private static final String ATTR_INSTALLER_UID = "installerUid";
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700129 private static final String ATTR_CREATED_MILLIS = "createdMillis";
130 private static final String ATTR_SESSION_STAGE_DIR = "sessionStageDir";
Jeff Sharkey742e7902014-08-16 19:09:13 -0700131 private static final String ATTR_SESSION_STAGE_CID = "sessionStageCid";
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700132 private static final String ATTR_PREPARED = "prepared";
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700133 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";
Jeff Sharkey02bd7842014-10-06 15:14:27 -0700139 @Deprecated
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700140 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";
Todd Kennedya1d12cf2015-09-29 15:43:00 -0700143 private static final String ATTR_ORIGINATING_UID = "originatingUid";
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700144 private static final String ATTR_REFERRER_URI = "referrerUri";
145 private static final String ATTR_ABI_OVERRIDE = "abiOverride";
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -0700146 private static final String ATTR_VOLUME_UUID = "volumeUuid";
Svet Ganov7121e182015-07-13 22:38:12 -0700147 private static final String ATTR_NAME = "name";
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700148
Jeff Sharkeyf174c6e2014-08-05 10:42:27 -0700149 /** Automatically destroy sessions older than this */
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700150 private static final long MAX_AGE_MILLIS = 3 * DateUtils.DAY_IN_MILLIS;
Jeff Sharkeyf174c6e2014-08-05 10:42:27 -0700151 /** Upper bound on number of active sessions for a UID */
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700152 private static final long MAX_ACTIVE_SESSIONS = 1024;
Jeff Sharkeyf174c6e2014-08-05 10:42:27 -0700153 /** Upper bound on number of historical sessions for a UID */
154 private static final long MAX_HISTORICAL_SESSIONS = 1048576;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700155
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700156 private final Context mContext;
157 private final PackageManagerService mPm;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700158
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -0700159 private AppOpsManager mAppOps;
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -0700160
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700161 private final HandlerThread mInstallThread;
Jeff Sharkeycbf47912014-09-12 09:55:32 -0700162 private final Handler mInstallHandler;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700163
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700164 private final Callbacks mCallbacks;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700165
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700166 /**
Jeff Sharkey02bd7842014-10-06 15:14:27 -0700167 * File storing persisted {@link #mSessions} metadata.
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700168 */
169 private final AtomicFile mSessionsFile;
170
Jeff Sharkey02bd7842014-10-06 15:14:27 -0700171 /**
172 * Directory storing persisted {@link #mSessions} metadata which is too
173 * heavy to store directly in {@link #mSessionsFile}.
174 */
175 private final File mSessionsDir;
176
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700177 private final InternalCallback mInternalCallback = new InternalCallback();
178
179 /**
180 * Used for generating session IDs. Since this is created at boot time,
181 * normal random might be predictable.
182 */
183 private final Random mRandom = new SecureRandom();
184
Todd Kennedy28c4e802016-07-13 13:20:30 -0700185 /** All sessions allocated */
186 @GuardedBy("mSessions")
187 private final SparseBooleanArray mAllocatedSessions = new SparseBooleanArray();
188
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700189 @GuardedBy("mSessions")
190 private final SparseArray<PackageInstallerSession> mSessions = new SparseArray<>();
191
Jeff Sharkey9a445772014-07-16 11:32:08 -0700192 /** Historical sessions kept around for debugging purposes */
193 @GuardedBy("mSessions")
194 private final SparseArray<PackageInstallerSession> mHistoricalSessions = new SparseArray<>();
195
Jeff Sharkey742e7902014-08-16 19:09:13 -0700196 /** Sessions allocated to legacy users */
197 @GuardedBy("mSessions")
198 private final SparseBooleanArray mLegacySessions = new SparseBooleanArray();
199
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700200 private static final FilenameFilter sStageFilter = new FilenameFilter() {
201 @Override
202 public boolean accept(File dir, String name) {
Jeff Sharkey742e7902014-08-16 19:09:13 -0700203 return isStageName(name);
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700204 }
205 };
206
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -0700207 public PackageInstallerService(Context context, PackageManagerService pm) {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700208 mContext = context;
209 mPm = pm;
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700210
211 mInstallThread = new HandlerThread(TAG);
212 mInstallThread.start();
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700213
Jeff Sharkeycbf47912014-09-12 09:55:32 -0700214 mInstallHandler = new Handler(mInstallThread.getLooper());
215
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700216 mCallbacks = new Callbacks(mInstallThread.getLooper());
217
218 mSessionsFile = new AtomicFile(
Jeff Sharkey8212ae02016-02-10 14:46:43 -0700219 new File(Environment.getDataSystemDirectory(), "install_sessions.xml"));
220 mSessionsDir = new File(Environment.getDataSystemDirectory(), "install_sessions");
Jeff Sharkey02bd7842014-10-06 15:14:27 -0700221 mSessionsDir.mkdirs();
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700222
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700223 synchronized (mSessions) {
224 readSessionsLocked();
225
Todd Kennedy2699f062015-11-20 13:07:17 -0800226 reconcileStagesLocked(StorageManager.UUID_PRIVATE_INTERNAL, false /*isEphemeral*/);
227 reconcileStagesLocked(StorageManager.UUID_PRIVATE_INTERNAL, true /*isEphemeral*/);
Jeff Sharkey6dce4962015-07-03 18:08:41 -0700228
Jeff Sharkey54d42be2015-07-20 16:36:55 -0700229 final ArraySet<File> unclaimedIcons = newArraySet(
Jeff Sharkey02bd7842014-10-06 15:14:27 -0700230 mSessionsDir.listFiles());
Jeff Sharkey742e7902014-08-16 19:09:13 -0700231
Jeff Sharkey02bd7842014-10-06 15:14:27 -0700232 // Ignore stages and icons claimed by active sessions
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700233 for (int i = 0; i < mSessions.size(); i++) {
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700234 final PackageInstallerSession session = mSessions.valueAt(i);
Jeff Sharkey02bd7842014-10-06 15:14:27 -0700235 unclaimedIcons.remove(buildAppIconFile(session.sessionId));
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700236 }
Jeff Sharkey742e7902014-08-16 19:09:13 -0700237
Jeff Sharkey02bd7842014-10-06 15:14:27 -0700238 // Clean up orphaned icons
239 for (File icon : unclaimedIcons) {
240 Slog.w(TAG, "Deleting orphan icon " + icon);
241 icon.delete();
242 }
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700243 }
244 }
245
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -0700246 public void systemReady() {
247 mAppOps = mContext.getSystemService(AppOpsManager.class);
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -0700248 }
249
Todd Kennedy2699f062015-11-20 13:07:17 -0800250 private void reconcileStagesLocked(String volumeUuid, boolean isEphemeral) {
251 final File stagingDir = buildStagingDir(volumeUuid, isEphemeral);
Jeff Sharkey54d42be2015-07-20 16:36:55 -0700252 final ArraySet<File> unclaimedStages = newArraySet(
Jeff Sharkey6dce4962015-07-03 18:08:41 -0700253 stagingDir.listFiles(sStageFilter));
254
255 // Ignore stages claimed by active sessions
256 for (int i = 0; i < mSessions.size(); i++) {
257 final PackageInstallerSession session = mSessions.valueAt(i);
258 unclaimedStages.remove(session.stageDir);
259 }
260
261 // Clean up orphaned staging directories
262 for (File stage : unclaimedStages) {
263 Slog.w(TAG, "Deleting orphan stage " + stage);
264 synchronized (mPm.mInstallLock) {
Jeff Sharkeyfdeeeea2016-01-11 17:34:24 -0700265 mPm.removeCodePathLI(stage);
Jeff Sharkey6dce4962015-07-03 18:08:41 -0700266 }
267 }
268 }
269
270 public void onPrivateVolumeMounted(String volumeUuid) {
271 synchronized (mSessions) {
Todd Kennedy2699f062015-11-20 13:07:17 -0800272 reconcileStagesLocked(volumeUuid, false /*isEphemeral*/);
Jeff Sharkey6dce4962015-07-03 18:08:41 -0700273 }
274 }
275
Jeff Sharkey742e7902014-08-16 19:09:13 -0700276 public void onSecureContainersAvailable() {
277 synchronized (mSessions) {
278 final ArraySet<String> unclaimed = new ArraySet<>();
279 for (String cid : PackageHelper.getSecureContainerList()) {
280 if (isStageName(cid)) {
281 unclaimed.add(cid);
282 }
283 }
284
285 // Ignore stages claimed by active sessions
286 for (int i = 0; i < mSessions.size(); i++) {
287 final PackageInstallerSession session = mSessions.valueAt(i);
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700288 final String cid = session.stageCid;
Jeff Sharkey742e7902014-08-16 19:09:13 -0700289
290 if (unclaimed.remove(cid)) {
291 // Claimed by active session, mount it
292 PackageHelper.mountSdDir(cid, PackageManagerService.getEncryptKey(),
293 Process.SYSTEM_UID);
294 }
295 }
296
297 // Clean up orphaned staging containers
298 for (String cid : unclaimed) {
299 Slog.w(TAG, "Deleting orphan container " + cid);
300 PackageHelper.destroySdDir(cid);
301 }
302 }
303 }
304
305 public static boolean isStageName(String name) {
306 final boolean isFile = name.startsWith("vmdl") && name.endsWith(".tmp");
307 final boolean isContainer = name.startsWith("smdl") && name.endsWith(".tmp");
308 final boolean isLegacyContainer = name.startsWith("smdl2tmp");
309 return isFile || isContainer || isLegacyContainer;
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700310 }
311
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700312 @Deprecated
Todd Kennedy2699f062015-11-20 13:07:17 -0800313 public File allocateStageDirLegacy(String volumeUuid, boolean isEphemeral) throws IOException {
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700314 synchronized (mSessions) {
315 try {
316 final int sessionId = allocateSessionIdLocked();
Jeff Sharkey742e7902014-08-16 19:09:13 -0700317 mLegacySessions.put(sessionId, true);
Todd Kennedy2699f062015-11-20 13:07:17 -0800318 final File stageDir = buildStageDir(volumeUuid, sessionId, isEphemeral);
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -0700319 prepareStageDir(stageDir);
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700320 return stageDir;
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700321 } catch (IllegalStateException e) {
322 throw new IOException(e);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700323 }
324 }
325 }
326
Jeff Sharkey742e7902014-08-16 19:09:13 -0700327 @Deprecated
328 public String allocateExternalStageCidLegacy() {
329 synchronized (mSessions) {
330 final int sessionId = allocateSessionIdLocked();
331 mLegacySessions.put(sessionId, true);
332 return "smdl" + sessionId + ".tmp";
333 }
334 }
335
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700336 private void readSessionsLocked() {
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700337 if (LOGD) Slog.v(TAG, "readSessionsLocked()");
338
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700339 mSessions.clear();
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700340
341 FileInputStream fis = null;
342 try {
343 fis = mSessionsFile.openRead();
344 final XmlPullParser in = Xml.newPullParser();
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +0100345 in.setInput(fis, StandardCharsets.UTF_8.name());
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700346
347 int type;
348 while ((type = in.next()) != END_DOCUMENT) {
349 if (type == START_TAG) {
350 final String tag = in.getName();
351 if (TAG_SESSION.equals(tag)) {
352 final PackageInstallerSession session = readSessionLocked(in);
353 final long age = System.currentTimeMillis() - session.createdMillis;
354
355 final boolean valid;
356 if (age >= MAX_AGE_MILLIS) {
357 Slog.w(TAG, "Abandoning old session first created at "
358 + session.createdMillis);
359 valid = false;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700360 } else {
361 valid = true;
362 }
363
364 if (valid) {
365 mSessions.put(session.sessionId, session);
366 } else {
367 // Since this is early during boot we don't send
368 // any observer events about the session, but we
369 // keep details around for dumpsys.
370 mHistoricalSessions.put(session.sessionId, session);
371 }
Todd Kennedy28c4e802016-07-13 13:20:30 -0700372 mAllocatedSessions.put(session.sessionId, true);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700373 }
374 }
375 }
376 } catch (FileNotFoundException e) {
377 // Missing sessions are okay, probably first boot
Svet Ganov7121e182015-07-13 22:38:12 -0700378 } catch (IOException | XmlPullParserException e) {
Dianne Hackborn8d051722014-10-01 14:59:58 -0700379 Slog.wtf(TAG, "Failed reading install sessions", e);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700380 } finally {
381 IoUtils.closeQuietly(fis);
382 }
383 }
384
Svet Ganov7121e182015-07-13 22:38:12 -0700385 private PackageInstallerSession readSessionLocked(XmlPullParser in) throws IOException,
386 XmlPullParserException {
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700387 final int sessionId = readIntAttribute(in, ATTR_SESSION_ID);
388 final int userId = readIntAttribute(in, ATTR_USER_ID);
389 final String installerPackageName = readStringAttribute(in, ATTR_INSTALLER_PACKAGE_NAME);
Jeff Sharkeycd654482016-01-08 17:42:11 -0700390 final int installerUid = readIntAttribute(in, ATTR_INSTALLER_UID, mPm.getPackageUid(
391 installerPackageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId));
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700392 final long createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS);
Jeff Sharkey742e7902014-08-16 19:09:13 -0700393 final String stageDirRaw = readStringAttribute(in, ATTR_SESSION_STAGE_DIR);
394 final File stageDir = (stageDirRaw != null) ? new File(stageDirRaw) : null;
395 final String stageCid = readStringAttribute(in, ATTR_SESSION_STAGE_CID);
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700396 final boolean prepared = readBooleanAttribute(in, ATTR_PREPARED, true);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700397 final boolean sealed = readBooleanAttribute(in, ATTR_SEALED);
398
Jeff Sharkeya0907432014-08-15 10:23:11 -0700399 final SessionParams params = new SessionParams(
400 SessionParams.MODE_INVALID);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700401 params.mode = readIntAttribute(in, ATTR_MODE);
402 params.installFlags = readIntAttribute(in, ATTR_INSTALL_FLAGS);
403 params.installLocation = readIntAttribute(in, ATTR_INSTALL_LOCATION);
404 params.sizeBytes = readLongAttribute(in, ATTR_SIZE_BYTES);
405 params.appPackageName = readStringAttribute(in, ATTR_APP_PACKAGE_NAME);
406 params.appIcon = readBitmapAttribute(in, ATTR_APP_ICON);
407 params.appLabel = readStringAttribute(in, ATTR_APP_LABEL);
408 params.originatingUri = readUriAttribute(in, ATTR_ORIGINATING_URI);
Todd Kennedya1d12cf2015-09-29 15:43:00 -0700409 params.originatingUid =
410 readIntAttribute(in, ATTR_ORIGINATING_UID, SessionParams.UID_UNKNOWN);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700411 params.referrerUri = readUriAttribute(in, ATTR_REFERRER_URI);
412 params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE);
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -0700413 params.volumeUuid = readStringAttribute(in, ATTR_VOLUME_UUID);
Svet Ganov7121e182015-07-13 22:38:12 -0700414 params.grantedRuntimePermissions = readGrantedRuntimePermissions(in);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700415
Jeff Sharkey02bd7842014-10-06 15:14:27 -0700416 final File appIconFile = buildAppIconFile(sessionId);
417 if (appIconFile.exists()) {
418 params.appIcon = BitmapFactory.decodeFile(appIconFile.getAbsolutePath());
419 params.appIconLastModified = appIconFile.lastModified();
420 }
421
Jeff Sharkeya0907432014-08-15 10:23:11 -0700422 return new PackageInstallerSession(mInternalCallback, mContext, mPm,
Jeff Sharkeye9808042014-09-11 21:15:37 -0700423 mInstallThread.getLooper(), sessionId, userId, installerPackageName, installerUid,
424 params, createdMillis, stageDir, stageCid, prepared, sealed);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700425 }
426
427 private void writeSessionsLocked() {
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700428 if (LOGD) Slog.v(TAG, "writeSessionsLocked()");
429
430 FileOutputStream fos = null;
431 try {
432 fos = mSessionsFile.startWrite();
433
434 XmlSerializer out = new FastXmlSerializer();
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +0100435 out.setOutput(fos, StandardCharsets.UTF_8.name());
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700436 out.startDocument(null, true);
437 out.startTag(null, TAG_SESSIONS);
438 final int size = mSessions.size();
439 for (int i = 0; i < size; i++) {
440 final PackageInstallerSession session = mSessions.valueAt(i);
441 writeSessionLocked(out, session);
442 }
443 out.endTag(null, TAG_SESSIONS);
444 out.endDocument();
445
446 mSessionsFile.finishWrite(fos);
447 } catch (IOException e) {
448 if (fos != null) {
449 mSessionsFile.failWrite(fos);
450 }
451 }
452 }
453
454 private void writeSessionLocked(XmlSerializer out, PackageInstallerSession session)
455 throws IOException {
Jeff Sharkeya0907432014-08-15 10:23:11 -0700456 final SessionParams params = session.params;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700457
458 out.startTag(null, TAG_SESSION);
459
460 writeIntAttribute(out, ATTR_SESSION_ID, session.sessionId);
461 writeIntAttribute(out, ATTR_USER_ID, session.userId);
462 writeStringAttribute(out, ATTR_INSTALLER_PACKAGE_NAME,
463 session.installerPackageName);
Jeff Sharkeye9808042014-09-11 21:15:37 -0700464 writeIntAttribute(out, ATTR_INSTALLER_UID, session.installerUid);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700465 writeLongAttribute(out, ATTR_CREATED_MILLIS, session.createdMillis);
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700466 if (session.stageDir != null) {
Jeff Sharkey742e7902014-08-16 19:09:13 -0700467 writeStringAttribute(out, ATTR_SESSION_STAGE_DIR,
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700468 session.stageDir.getAbsolutePath());
Jeff Sharkey742e7902014-08-16 19:09:13 -0700469 }
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700470 if (session.stageCid != null) {
471 writeStringAttribute(out, ATTR_SESSION_STAGE_CID, session.stageCid);
Jeff Sharkey742e7902014-08-16 19:09:13 -0700472 }
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700473 writeBooleanAttribute(out, ATTR_PREPARED, session.isPrepared());
Jeff Sharkey742e7902014-08-16 19:09:13 -0700474 writeBooleanAttribute(out, ATTR_SEALED, session.isSealed());
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700475
476 writeIntAttribute(out, ATTR_MODE, params.mode);
477 writeIntAttribute(out, ATTR_INSTALL_FLAGS, params.installFlags);
478 writeIntAttribute(out, ATTR_INSTALL_LOCATION, params.installLocation);
479 writeLongAttribute(out, ATTR_SIZE_BYTES, params.sizeBytes);
480 writeStringAttribute(out, ATTR_APP_PACKAGE_NAME, params.appPackageName);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700481 writeStringAttribute(out, ATTR_APP_LABEL, params.appLabel);
482 writeUriAttribute(out, ATTR_ORIGINATING_URI, params.originatingUri);
Todd Kennedya1d12cf2015-09-29 15:43:00 -0700483 writeIntAttribute(out, ATTR_ORIGINATING_UID, params.originatingUid);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700484 writeUriAttribute(out, ATTR_REFERRER_URI, params.referrerUri);
485 writeStringAttribute(out, ATTR_ABI_OVERRIDE, params.abiOverride);
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -0700486 writeStringAttribute(out, ATTR_VOLUME_UUID, params.volumeUuid);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700487
Jeff Sharkey02bd7842014-10-06 15:14:27 -0700488 // Persist app icon if changed since last written
489 final File appIconFile = buildAppIconFile(session.sessionId);
490 if (params.appIcon == null && appIconFile.exists()) {
491 appIconFile.delete();
492 } else if (params.appIcon != null
493 && appIconFile.lastModified() != params.appIconLastModified) {
494 if (LOGD) Slog.w(TAG, "Writing changed icon " + appIconFile);
495 FileOutputStream os = null;
496 try {
497 os = new FileOutputStream(appIconFile);
498 params.appIcon.compress(CompressFormat.PNG, 90, os);
499 } catch (IOException e) {
500 Slog.w(TAG, "Failed to write icon " + appIconFile + ": " + e.getMessage());
501 } finally {
502 IoUtils.closeQuietly(os);
503 }
504
505 params.appIconLastModified = appIconFile.lastModified();
506 }
507
Svet Ganov7121e182015-07-13 22:38:12 -0700508 writeGrantedRuntimePermissions(out, params.grantedRuntimePermissions);
509
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700510 out.endTag(null, TAG_SESSION);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700511 }
512
Svet Ganov7121e182015-07-13 22:38:12 -0700513 private static void writeGrantedRuntimePermissions(XmlSerializer out,
514 String[] grantedRuntimePermissions) throws IOException {
515 if (grantedRuntimePermissions != null) {
516 for (String permission : grantedRuntimePermissions) {
517 out.startTag(null, TAG_GRANTED_RUNTIME_PERMISSION);
518 writeStringAttribute(out, ATTR_NAME, permission);
519 out.endTag(null, TAG_GRANTED_RUNTIME_PERMISSION);
520 }
521 }
522 }
523
524 private static String[] readGrantedRuntimePermissions(XmlPullParser in)
525 throws IOException, XmlPullParserException {
526 List<String> permissions = null;
527
528 final int outerDepth = in.getDepth();
529 int type;
530 while ((type = in.next()) != XmlPullParser.END_DOCUMENT
531 && (type != XmlPullParser.END_TAG || in.getDepth() > outerDepth)) {
532 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
533 continue;
534 }
535 if (TAG_GRANTED_RUNTIME_PERMISSION.equals(in.getName())) {
536 String permission = readStringAttribute(in, ATTR_NAME);
537 if (permissions == null) {
538 permissions = new ArrayList<>();
539 }
540 permissions.add(permission);
541 }
542 }
543
544 if (permissions == null) {
545 return null;
546 }
547
548 String[] permissionsArray = new String[permissions.size()];
549 permissions.toArray(permissionsArray);
550 return permissionsArray;
551 }
552
Jeff Sharkey02bd7842014-10-06 15:14:27 -0700553 private File buildAppIconFile(int sessionId) {
554 return new File(mSessionsDir, "app_icon." + sessionId + ".png");
555 }
556
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700557 private void writeSessionsAsync() {
558 IoThread.getHandler().post(new Runnable() {
559 @Override
560 public void run() {
561 synchronized (mSessions) {
562 writeSessionsLocked();
563 }
564 }
565 });
566 }
567
568 @Override
Jeff Sharkeya0907432014-08-15 10:23:11 -0700569 public int createSession(SessionParams params, String installerPackageName, int userId) {
Jeff Sharkey742e7902014-08-16 19:09:13 -0700570 try {
571 return createSessionInternal(params, installerPackageName, userId);
572 } catch (IOException e) {
573 throw ExceptionUtils.wrap(e);
574 }
575 }
576
577 private int createSessionInternal(SessionParams params, String installerPackageName, int userId)
578 throws IOException {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700579 final int callingUid = Binder.getCallingUid();
Jeff Sharkeye9808042014-09-11 21:15:37 -0700580 mPm.enforceCrossUserPermission(callingUid, userId, true, true, "createSession");
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700581
Jeff Sharkeye9808042014-09-11 21:15:37 -0700582 if (mPm.isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700583 throw new SecurityException("User restriction prevents installing");
584 }
585
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700586 if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {
Jeff Sharkeye9808042014-09-11 21:15:37 -0700587 params.installFlags |= PackageManager.INSTALL_FROM_ADB;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700588
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700589 } else {
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700590 mAppOps.checkPackage(callingUid, installerPackageName);
591
Jeff Sharkeye9808042014-09-11 21:15:37 -0700592 params.installFlags &= ~PackageManager.INSTALL_FROM_ADB;
593 params.installFlags &= ~PackageManager.INSTALL_ALL_USERS;
594 params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700595 }
596
Svetoslav805b63e2015-04-09 17:28:54 -0700597 // Only system components can circumvent runtime permissions when installing.
598 if ((params.installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0
599 && mContext.checkCallingOrSelfPermission(Manifest.permission
600 .INSTALL_GRANT_RUNTIME_PERMISSIONS) == PackageManager.PERMISSION_DENIED) {
601 throw new SecurityException("You need the "
602 + "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission "
603 + "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag");
604 }
605
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700606 // Defensively resize giant app icons
607 if (params.appIcon != null) {
608 final ActivityManager am = (ActivityManager) mContext.getSystemService(
609 Context.ACTIVITY_SERVICE);
610 final int iconSize = am.getLauncherLargeIconSize();
611 if ((params.appIcon.getWidth() > iconSize * 2)
612 || (params.appIcon.getHeight() > iconSize * 2)) {
613 params.appIcon = Bitmap.createScaledBitmap(params.appIcon, iconSize, iconSize,
614 true);
615 }
616 }
617
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -0700618 switch (params.mode) {
619 case SessionParams.MODE_FULL_INSTALL:
620 case SessionParams.MODE_INHERIT_EXISTING:
621 break;
622 default:
623 throw new IllegalArgumentException("Invalid install mode: " + params.mode);
624 }
625
626 // If caller requested explicit location, sanity check it, otherwise
627 // resolve the best internal or adopted location.
628 if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
629 if (!PackageHelper.fitsOnInternal(mContext, params.sizeBytes)) {
630 throw new IOException("No suitable internal storage available");
631 }
632
633 } else if ((params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
634 if (!PackageHelper.fitsOnExternal(mContext, params.sizeBytes)) {
635 throw new IOException("No suitable external storage available");
636 }
637
Jeff Sharkeyab234092015-06-09 21:42:22 -0700638 } else if ((params.installFlags & PackageManager.INSTALL_FORCE_VOLUME_UUID) != 0) {
639 // For now, installs to adopted media are treated as internal from
640 // an install flag point-of-view.
641 params.setInstallFlagsInternal();
642
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -0700643 } else {
644 // For now, installs to adopted media are treated as internal from
645 // an install flag point-of-view.
646 params.setInstallFlagsInternal();
647
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700648 // Resolve best location for install, based on combination of
649 // requested install flags, delta size, and manifest settings.
Robin Leee812d902014-08-21 16:51:18 +0100650 final long ident = Binder.clearCallingIdentity();
651 try {
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -0700652 params.volumeUuid = PackageHelper.resolveInstallVolume(mContext,
653 params.appPackageName, params.installLocation, params.sizeBytes);
Robin Leee812d902014-08-21 16:51:18 +0100654 } finally {
655 Binder.restoreCallingIdentity(ident);
656 }
Jeff Sharkeya1031142014-07-12 18:09:46 -0700657 }
658
659 final int sessionId;
660 final PackageInstallerSession session;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700661 synchronized (mSessions) {
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700662 // Sanity check that installer isn't going crazy
Jeff Sharkeyf174c6e2014-08-05 10:42:27 -0700663 final int activeCount = getSessionCount(mSessions, callingUid);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700664 if (activeCount >= MAX_ACTIVE_SESSIONS) {
Jeff Sharkeyf174c6e2014-08-05 10:42:27 -0700665 throw new IllegalStateException(
666 "Too many active sessions for UID " + callingUid);
667 }
668 final int historicalCount = getSessionCount(mHistoricalSessions, callingUid);
669 if (historicalCount >= MAX_HISTORICAL_SESSIONS) {
670 throw new IllegalStateException(
671 "Too many historical sessions for UID " + callingUid);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700672 }
673
Jeff Sharkeya1031142014-07-12 18:09:46 -0700674 sessionId = allocateSessionIdLocked();
Todd Kennedy04918fe2016-07-12 14:07:40 -0700675 }
Jeff Sharkeya1031142014-07-12 18:09:46 -0700676
Todd Kennedy04918fe2016-07-12 14:07:40 -0700677 final long createdMillis = System.currentTimeMillis();
678 // We're staging to exactly one location
679 File stageDir = null;
680 String stageCid = null;
681 if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
682 final boolean isEphemeral =
683 (params.installFlags & PackageManager.INSTALL_EPHEMERAL) != 0;
684 stageDir = buildStageDir(params.volumeUuid, sessionId, isEphemeral);
685 } else {
686 stageCid = buildExternalStageCid(sessionId);
687 }
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700688
Todd Kennedy04918fe2016-07-12 14:07:40 -0700689 session = new PackageInstallerSession(mInternalCallback, mContext, mPm,
690 mInstallThread.getLooper(), sessionId, userId, installerPackageName, callingUid,
691 params, createdMillis, stageDir, stageCid, false, false);
692
693 synchronized (mSessions) {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700694 mSessions.put(sessionId, session);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700695 }
Jeff Sharkeya1031142014-07-12 18:09:46 -0700696
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700697 mCallbacks.notifySessionCreated(session.sessionId, session.userId);
Jeff Sharkeya1031142014-07-12 18:09:46 -0700698 writeSessionsAsync();
699 return sessionId;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700700 }
701
Jeff Sharkey381d94b2014-08-24 14:45:56 -0700702 @Override
Jeff Sharkeyec9bad22014-09-05 09:45:20 -0700703 public void updateSessionAppIcon(int sessionId, Bitmap appIcon) {
704 synchronized (mSessions) {
705 final PackageInstallerSession session = mSessions.get(sessionId);
706 if (session == null || !isCallingUidOwner(session)) {
707 throw new SecurityException("Caller has no access to session " + sessionId);
708 }
Jeff Sharkey02bd7842014-10-06 15:14:27 -0700709
710 // Defensively resize giant app icons
711 if (appIcon != null) {
712 final ActivityManager am = (ActivityManager) mContext.getSystemService(
713 Context.ACTIVITY_SERVICE);
714 final int iconSize = am.getLauncherLargeIconSize();
715 if ((appIcon.getWidth() > iconSize * 2)
716 || (appIcon.getHeight() > iconSize * 2)) {
717 appIcon = Bitmap.createScaledBitmap(appIcon, iconSize, iconSize, true);
718 }
719 }
720
Jeff Sharkeyec9bad22014-09-05 09:45:20 -0700721 session.params.appIcon = appIcon;
Jeff Sharkey02bd7842014-10-06 15:14:27 -0700722 session.params.appIconLastModified = -1;
723
Jeff Sharkeyec9bad22014-09-05 09:45:20 -0700724 mInternalCallback.onSessionBadgingChanged(session);
725 }
726 }
727
728 @Override
729 public void updateSessionAppLabel(int sessionId, String appLabel) {
730 synchronized (mSessions) {
731 final PackageInstallerSession session = mSessions.get(sessionId);
732 if (session == null || !isCallingUidOwner(session)) {
733 throw new SecurityException("Caller has no access to session " + sessionId);
734 }
735 session.params.appLabel = appLabel;
736 mInternalCallback.onSessionBadgingChanged(session);
737 }
738 }
739
740 @Override
Jeff Sharkey381d94b2014-08-24 14:45:56 -0700741 public void abandonSession(int sessionId) {
742 synchronized (mSessions) {
743 final PackageInstallerSession session = mSessions.get(sessionId);
744 if (session == null || !isCallingUidOwner(session)) {
745 throw new SecurityException("Caller has no access to session " + sessionId);
746 }
747 session.abandon();
748 }
749 }
750
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700751 @Override
752 public IPackageInstallerSession openSession(int sessionId) {
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700753 try {
754 return openSessionInternal(sessionId);
755 } catch (IOException e) {
756 throw ExceptionUtils.wrap(e);
757 }
758 }
759
760 private IPackageInstallerSession openSessionInternal(int sessionId) throws IOException {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700761 synchronized (mSessions) {
762 final PackageInstallerSession session = mSessions.get(sessionId);
Jeff Sharkey381d94b2014-08-24 14:45:56 -0700763 if (session == null || !isCallingUidOwner(session)) {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700764 throw new SecurityException("Caller has no access to session " + sessionId);
765 }
Jeff Sharkey742e7902014-08-16 19:09:13 -0700766 session.open();
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700767 return session;
768 }
769 }
770
771 private int allocateSessionIdLocked() {
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700772 int n = 0;
773 int sessionId;
774 do {
Jeff Sharkeyf174c6e2014-08-05 10:42:27 -0700775 sessionId = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
Todd Kennedy28c4e802016-07-13 13:20:30 -0700776 if (!mAllocatedSessions.get(sessionId, false)) {
777 mAllocatedSessions.put(sessionId, true);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700778 return sessionId;
779 }
780 } while (n++ < 32);
781
782 throw new IllegalStateException("Failed to allocate session ID");
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700783 }
784
Todd Kennedy2699f062015-11-20 13:07:17 -0800785 private File buildStagingDir(String volumeUuid, boolean isEphemeral) {
786 if (isEphemeral) {
787 return Environment.getDataAppEphemeralDirectory(volumeUuid);
788 }
Jeff Sharkey6dce4962015-07-03 18:08:41 -0700789 return Environment.getDataAppDirectory(volumeUuid);
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700790 }
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700791
Todd Kennedy2699f062015-11-20 13:07:17 -0800792 private File buildStageDir(String volumeUuid, int sessionId, boolean isEphemeral) {
793 final File stagingDir = buildStagingDir(volumeUuid, isEphemeral);
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -0700794 return new File(stagingDir, "vmdl" + sessionId + ".tmp");
795 }
796
797 static void prepareStageDir(File stageDir) throws IOException {
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700798 if (stageDir.exists()) {
799 throw new IOException("Session dir already exists: " + stageDir);
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700800 }
801
802 try {
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700803 Os.mkdir(stageDir.getAbsolutePath(), 0755);
804 Os.chmod(stageDir.getAbsolutePath(), 0755);
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700805 } catch (ErrnoException e) {
806 // This purposefully throws if directory already exists
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700807 throw new IOException("Failed to prepare session dir: " + stageDir, e);
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700808 }
809
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700810 if (!SELinux.restorecon(stageDir)) {
811 throw new IOException("Failed to restorecon session dir: " + stageDir);
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700812 }
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700813 }
814
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700815 private String buildExternalStageCid(int sessionId) {
816 return "smdl" + sessionId + ".tmp";
817 }
Jeff Sharkey742e7902014-08-16 19:09:13 -0700818
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700819 static void prepareExternalStageCid(String stageCid, long sizeBytes) throws IOException {
820 if (PackageHelper.createSdDir(sizeBytes, stageCid, PackageManagerService.getEncryptKey(),
Jeff Sharkey742e7902014-08-16 19:09:13 -0700821 Process.SYSTEM_UID, true) == null) {
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700822 throw new IOException("Failed to create session cid: " + stageCid);
Jeff Sharkey742e7902014-08-16 19:09:13 -0700823 }
Jeff Sharkey742e7902014-08-16 19:09:13 -0700824 }
825
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700826 @Override
Jeff Sharkeya0907432014-08-15 10:23:11 -0700827 public SessionInfo getSessionInfo(int sessionId) {
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700828 synchronized (mSessions) {
829 final PackageInstallerSession session = mSessions.get(sessionId);
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700830 return session != null ? session.generateInfo() : null;
831 }
832 }
833
834 @Override
Jeff Sharkey97d47ed2014-10-15 09:19:47 -0700835 public ParceledListSlice<SessionInfo> getAllSessions(int userId) {
Amith Yamasani8cd28b52014-06-08 17:54:27 -0700836 mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "getAllSessions");
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700837
Jeff Sharkeya0907432014-08-15 10:23:11 -0700838 final List<SessionInfo> result = new ArrayList<>();
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700839 synchronized (mSessions) {
840 for (int i = 0; i < mSessions.size(); i++) {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700841 final PackageInstallerSession session = mSessions.valueAt(i);
Jeff Sharkeybb580672014-07-10 12:10:25 -0700842 if (session.userId == userId) {
843 result.add(session.generateInfo());
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700844 }
845 }
846 }
Jeff Sharkey97d47ed2014-10-15 09:19:47 -0700847 return new ParceledListSlice<>(result);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700848 }
849
850 @Override
Jeff Sharkey97d47ed2014-10-15 09:19:47 -0700851 public ParceledListSlice<SessionInfo> getMySessions(String installerPackageName, int userId) {
Amith Yamasani8cd28b52014-06-08 17:54:27 -0700852 mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "getMySessions");
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700853 mAppOps.checkPackage(Binder.getCallingUid(), installerPackageName);
854
Jeff Sharkeya0907432014-08-15 10:23:11 -0700855 final List<SessionInfo> result = new ArrayList<>();
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700856 synchronized (mSessions) {
857 for (int i = 0; i < mSessions.size(); i++) {
858 final PackageInstallerSession session = mSessions.valueAt(i);
859 if (Objects.equals(session.installerPackageName, installerPackageName)
860 && session.userId == userId) {
861 result.add(session.generateInfo());
862 }
863 }
864 }
Jeff Sharkey97d47ed2014-10-15 09:19:47 -0700865 return new ParceledListSlice<>(result);
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700866 }
867
868 @Override
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000869 public void uninstall(String packageName, String callerPackageName, int flags,
870 IntentSender statusReceiver, int userId) {
871 final int callingUid = Binder.getCallingUid();
872 mPm.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall");
873 if ((callingUid != Process.SHELL_UID) && (callingUid != Process.ROOT_UID)) {
874 mAppOps.checkPackage(callingUid, callerPackageName);
875 }
876
Makoto Onukic8a5a552015-11-19 14:29:12 -0800877 // Check whether the caller is device owner, in which case we do it silently.
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000878 DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(
879 Context.DEVICE_POLICY_SERVICE);
Makoto Onukic8a5a552015-11-19 14:29:12 -0800880 boolean isDeviceOwner = (dpm != null) && dpm.isDeviceOwnerAppOnCallingUser(
881 callerPackageName);
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700882
Jeff Sharkeya0907432014-08-15 10:23:11 -0700883 final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext,
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000884 statusReceiver, packageName, isDeviceOwner, userId);
Sudheer Shanka72de4dd2016-07-22 15:46:37 -0700885 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DELETE_PACKAGES)
886 == PackageManager.PERMISSION_GRANTED) {
Jeff Sharkeyf0600952014-08-07 17:31:53 -0700887 // Sweet, call straight through!
Jeff Sharkeya0907432014-08-15 10:23:11 -0700888 mPm.deletePackage(packageName, adapter.getBinder(), userId, flags);
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000889 } else if (isDeviceOwner) {
890 // Allow the DeviceOwner to silently delete packages
891 // Need to clear the calling identity to get DELETE_PACKAGES permission
892 long ident = Binder.clearCallingIdentity();
893 try {
894 mPm.deletePackage(packageName, adapter.getBinder(), userId, flags);
895 } finally {
896 Binder.restoreCallingIdentity(ident);
897 }
Jeff Sharkeyf0600952014-08-07 17:31:53 -0700898 } else {
899 // Take a short detour to confirm with user
900 final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
901 intent.setData(Uri.fromParts("package", packageName, null));
Jeff Sharkeya0907432014-08-15 10:23:11 -0700902 intent.putExtra(PackageInstaller.EXTRA_CALLBACK, adapter.getBinder().asBinder());
903 adapter.onUserActionRequired(intent);
Jeff Sharkeyf0600952014-08-07 17:31:53 -0700904 }
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700905 }
906
907 @Override
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700908 public void setPermissionsResult(int sessionId, boolean accepted) {
909 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, TAG);
910
911 synchronized (mSessions) {
Svet Ganov3baa8762016-04-08 09:22:54 -0700912 PackageInstallerSession session = mSessions.get(sessionId);
913 if (session != null) {
914 session.setPermissionsResult(accepted);
915 }
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700916 }
917 }
918
919 @Override
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700920 public void registerCallback(IPackageInstallerCallback callback, int userId) {
Amith Yamasani8cd28b52014-06-08 17:54:27 -0700921 mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "registerCallback");
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700922 mCallbacks.register(callback, userId);
Jeff Sharkeybb580672014-07-10 12:10:25 -0700923 }
924
925 @Override
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700926 public void unregisterCallback(IPackageInstallerCallback callback) {
927 mCallbacks.unregister(callback);
Jeff Sharkeya1031142014-07-12 18:09:46 -0700928 }
929
Jeff Sharkeyf174c6e2014-08-05 10:42:27 -0700930 private static int getSessionCount(SparseArray<PackageInstallerSession> sessions,
931 int installerUid) {
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700932 int count = 0;
Jeff Sharkeyf174c6e2014-08-05 10:42:27 -0700933 final int size = sessions.size();
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700934 for (int i = 0; i < size; i++) {
Jeff Sharkeyf174c6e2014-08-05 10:42:27 -0700935 final PackageInstallerSession session = sessions.valueAt(i);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700936 if (session.installerUid == installerUid) {
937 count++;
938 }
939 }
940 return count;
941 }
942
943 private boolean isCallingUidOwner(PackageInstallerSession session) {
944 final int callingUid = Binder.getCallingUid();
945 if (callingUid == Process.ROOT_UID) {
946 return true;
947 } else {
948 return (session != null) && (callingUid == session.installerUid);
Jeff Sharkeya1031142014-07-12 18:09:46 -0700949 }
950 }
951
Jeff Sharkeya0907432014-08-15 10:23:11 -0700952 static class PackageDeleteObserverAdapter extends PackageDeleteObserver {
953 private final Context mContext;
954 private final IntentSender mTarget;
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -0700955 private final String mPackageName;
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000956 private final Notification mNotification;
Jeff Sharkeya0907432014-08-15 10:23:11 -0700957
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -0700958 public PackageDeleteObserverAdapter(Context context, IntentSender target,
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000959 String packageName, boolean showNotification, int userId) {
Jeff Sharkeya0907432014-08-15 10:23:11 -0700960 mContext = context;
961 mTarget = target;
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -0700962 mPackageName = packageName;
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000963 if (showNotification) {
964 mNotification = buildSuccessNotification(mContext,
965 mContext.getResources().getString(R.string.package_deleted_device_owner),
966 packageName,
967 userId);
968 } else {
969 mNotification = null;
970 }
Jeff Sharkeya0907432014-08-15 10:23:11 -0700971 }
972
973 @Override
974 public void onUserActionRequired(Intent intent) {
975 final Intent fillIn = new Intent();
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -0700976 fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, mPackageName);
Jeff Sharkeya0907432014-08-15 10:23:11 -0700977 fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
Jeff Sharkey742e7902014-08-16 19:09:13 -0700978 PackageInstaller.STATUS_PENDING_USER_ACTION);
Jeff Sharkeya0907432014-08-15 10:23:11 -0700979 fillIn.putExtra(Intent.EXTRA_INTENT, intent);
980 try {
981 mTarget.sendIntent(mContext, 0, fillIn, null, null);
982 } catch (SendIntentException ignored) {
983 }
984 }
985
986 @Override
987 public void onPackageDeleted(String basePackageName, int returnCode, String msg) {
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000988 if (PackageManager.DELETE_SUCCEEDED == returnCode && mNotification != null) {
989 NotificationManager notificationManager = (NotificationManager)
990 mContext.getSystemService(Context.NOTIFICATION_SERVICE);
991 notificationManager.notify(basePackageName, 0, mNotification);
992 }
Jeff Sharkeya0907432014-08-15 10:23:11 -0700993 final Intent fillIn = new Intent();
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -0700994 fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, mPackageName);
Jeff Sharkeya0907432014-08-15 10:23:11 -0700995 fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
996 PackageManager.deleteStatusToPublicStatus(returnCode));
997 fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
998 PackageManager.deleteStatusToString(returnCode, msg));
999 fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode);
1000 try {
1001 mTarget.sendIntent(mContext, 0, fillIn, null, null);
1002 } catch (SendIntentException ignored) {
1003 }
1004 }
1005 }
1006
1007 static class PackageInstallObserverAdapter extends PackageInstallObserver {
1008 private final Context mContext;
1009 private final IntentSender mTarget;
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -07001010 private final int mSessionId;
Benjamin Franz39fb7fd2015-02-18 16:11:18 +00001011 private final boolean mShowNotification;
1012 private final int mUserId;
Jeff Sharkeya0907432014-08-15 10:23:11 -07001013
Benjamin Franz39fb7fd2015-02-18 16:11:18 +00001014 public PackageInstallObserverAdapter(Context context, IntentSender target, int sessionId,
1015 boolean showNotification, int userId) {
Jeff Sharkeya0907432014-08-15 10:23:11 -07001016 mContext = context;
1017 mTarget = target;
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -07001018 mSessionId = sessionId;
Benjamin Franz39fb7fd2015-02-18 16:11:18 +00001019 mShowNotification = showNotification;
1020 mUserId = userId;
Jeff Sharkeya0907432014-08-15 10:23:11 -07001021 }
1022
1023 @Override
1024 public void onUserActionRequired(Intent intent) {
1025 final Intent fillIn = new Intent();
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -07001026 fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId);
Jeff Sharkeya0907432014-08-15 10:23:11 -07001027 fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
Jeff Sharkey742e7902014-08-16 19:09:13 -07001028 PackageInstaller.STATUS_PENDING_USER_ACTION);
Jeff Sharkeya0907432014-08-15 10:23:11 -07001029 fillIn.putExtra(Intent.EXTRA_INTENT, intent);
1030 try {
1031 mTarget.sendIntent(mContext, 0, fillIn, null, null);
1032 } catch (SendIntentException ignored) {
1033 }
1034 }
1035
1036 @Override
1037 public void onPackageInstalled(String basePackageName, int returnCode, String msg,
1038 Bundle extras) {
Benjamin Franz39fb7fd2015-02-18 16:11:18 +00001039 if (PackageManager.INSTALL_SUCCEEDED == returnCode && mShowNotification) {
Benjamin Franz2e3e9432015-04-17 15:28:17 +01001040 boolean update = (extras != null) && extras.getBoolean(Intent.EXTRA_REPLACING);
Benjamin Franz39fb7fd2015-02-18 16:11:18 +00001041 Notification notification = buildSuccessNotification(mContext,
Benjamin Franz2e3e9432015-04-17 15:28:17 +01001042 mContext.getResources()
1043 .getString(update ? R.string.package_updated_device_owner :
1044 R.string.package_installed_device_owner),
Benjamin Franz39fb7fd2015-02-18 16:11:18 +00001045 basePackageName,
1046 mUserId);
1047 if (notification != null) {
1048 NotificationManager notificationManager = (NotificationManager)
1049 mContext.getSystemService(Context.NOTIFICATION_SERVICE);
1050 notificationManager.notify(basePackageName, 0, notification);
1051 }
1052 }
Jeff Sharkeya0907432014-08-15 10:23:11 -07001053 final Intent fillIn = new Intent();
Benjamin Franz2e3e9432015-04-17 15:28:17 +01001054 fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, basePackageName);
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -07001055 fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId);
Jeff Sharkeya0907432014-08-15 10:23:11 -07001056 fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
1057 PackageManager.installStatusToPublicStatus(returnCode));
1058 fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
1059 PackageManager.installStatusToString(returnCode, msg));
1060 fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode);
1061 if (extras != null) {
1062 final String existing = extras.getString(
1063 PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE);
1064 if (!TextUtils.isEmpty(existing)) {
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001065 fillIn.putExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME, existing);
Jeff Sharkeya0907432014-08-15 10:23:11 -07001066 }
1067 }
1068 try {
1069 mTarget.sendIntent(mContext, 0, fillIn, null, null);
1070 } catch (SendIntentException ignored) {
1071 }
1072 }
1073 }
1074
Benjamin Franz39fb7fd2015-02-18 16:11:18 +00001075 /**
1076 * Build a notification for package installation / deletion by device owners that is shown if
1077 * the operation succeeds.
1078 */
1079 private static Notification buildSuccessNotification(Context context, String contentText,
1080 String basePackageName, int userId) {
1081 PackageInfo packageInfo = null;
1082 try {
1083 packageInfo = AppGlobals.getPackageManager().getPackageInfo(
1084 basePackageName, 0, userId);
1085 } catch (RemoteException ignored) {
1086 }
1087 if (packageInfo == null || packageInfo.applicationInfo == null) {
1088 Slog.w(TAG, "Notification not built for package: " + basePackageName);
1089 return null;
1090 }
1091 PackageManager pm = context.getPackageManager();
1092 Bitmap packageIcon = ImageUtils.buildScaledBitmap(
1093 packageInfo.applicationInfo.loadIcon(pm),
1094 context.getResources().getDimensionPixelSize(
1095 android.R.dimen.notification_large_icon_width),
1096 context.getResources().getDimensionPixelSize(
1097 android.R.dimen.notification_large_icon_height));
1098 CharSequence packageLabel = packageInfo.applicationInfo.loadLabel(pm);
1099 return new Notification.Builder(context)
1100 .setSmallIcon(R.drawable.ic_check_circle_24px)
1101 .setColor(context.getResources().getColor(
1102 R.color.system_notification_accent_color))
1103 .setContentTitle(packageLabel)
1104 .setContentText(contentText)
Benjamin Franz2e3e9432015-04-17 15:28:17 +01001105 .setStyle(new Notification.BigTextStyle().bigText(contentText))
Benjamin Franz39fb7fd2015-02-18 16:11:18 +00001106 .setLargeIcon(packageIcon)
1107 .build();
1108 }
1109
Jeff Sharkey54d42be2015-07-20 16:36:55 -07001110 public static <E> ArraySet<E> newArraySet(E... elements) {
1111 final ArraySet<E> set = new ArraySet<E>();
1112 if (elements != null) {
1113 set.ensureCapacity(elements.length);
1114 Collections.addAll(set, elements);
1115 }
1116 return set;
1117 }
1118
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001119 private static class Callbacks extends Handler {
1120 private static final int MSG_SESSION_CREATED = 1;
Jeff Sharkeyec9bad22014-09-05 09:45:20 -07001121 private static final int MSG_SESSION_BADGING_CHANGED = 2;
Jeff Sharkeybc7bce32014-09-05 15:53:05 -07001122 private static final int MSG_SESSION_ACTIVE_CHANGED = 3;
Jeff Sharkeyec9bad22014-09-05 09:45:20 -07001123 private static final int MSG_SESSION_PROGRESS_CHANGED = 4;
Jeff Sharkeybc7bce32014-09-05 15:53:05 -07001124 private static final int MSG_SESSION_FINISHED = 5;
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -07001125
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001126 private final RemoteCallbackList<IPackageInstallerCallback>
1127 mCallbacks = new RemoteCallbackList<>();
Jeff Sharkeya1031142014-07-12 18:09:46 -07001128
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001129 public Callbacks(Looper looper) {
1130 super(looper);
1131 }
1132
1133 public void register(IPackageInstallerCallback callback, int userId) {
1134 mCallbacks.register(callback, new UserHandle(userId));
1135 }
1136
1137 public void unregister(IPackageInstallerCallback callback) {
1138 mCallbacks.unregister(callback);
1139 }
1140
1141 @Override
1142 public void handleMessage(Message msg) {
1143 final int userId = msg.arg2;
1144 final int n = mCallbacks.beginBroadcast();
1145 for (int i = 0; i < n; i++) {
1146 final IPackageInstallerCallback callback = mCallbacks.getBroadcastItem(i);
1147 final UserHandle user = (UserHandle) mCallbacks.getBroadcastCookie(i);
1148 // TODO: dispatch notifications for slave profiles
1149 if (userId == user.getIdentifier()) {
1150 try {
1151 invokeCallback(callback, msg);
1152 } catch (RemoteException ignored) {
1153 }
Jeff Sharkeya1031142014-07-12 18:09:46 -07001154 }
1155 }
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001156 mCallbacks.finishBroadcast();
Jeff Sharkeya1031142014-07-12 18:09:46 -07001157 }
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001158
1159 private void invokeCallback(IPackageInstallerCallback callback, Message msg)
1160 throws RemoteException {
1161 final int sessionId = msg.arg1;
1162 switch (msg.what) {
1163 case MSG_SESSION_CREATED:
1164 callback.onSessionCreated(sessionId);
1165 break;
Jeff Sharkeyec9bad22014-09-05 09:45:20 -07001166 case MSG_SESSION_BADGING_CHANGED:
1167 callback.onSessionBadgingChanged(sessionId);
1168 break;
Jeff Sharkeybc7bce32014-09-05 15:53:05 -07001169 case MSG_SESSION_ACTIVE_CHANGED:
1170 callback.onSessionActiveChanged(sessionId, (boolean) msg.obj);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001171 break;
1172 case MSG_SESSION_PROGRESS_CHANGED:
1173 callback.onSessionProgressChanged(sessionId, (float) msg.obj);
1174 break;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001175 case MSG_SESSION_FINISHED:
1176 callback.onSessionFinished(sessionId, (boolean) msg.obj);
1177 break;
1178 }
1179 }
1180
1181 private void notifySessionCreated(int sessionId, int userId) {
1182 obtainMessage(MSG_SESSION_CREATED, sessionId, userId).sendToTarget();
1183 }
1184
Jeff Sharkeyec9bad22014-09-05 09:45:20 -07001185 private void notifySessionBadgingChanged(int sessionId, int userId) {
1186 obtainMessage(MSG_SESSION_BADGING_CHANGED, sessionId, userId).sendToTarget();
1187 }
1188
Jeff Sharkeybc7bce32014-09-05 15:53:05 -07001189 private void notifySessionActiveChanged(int sessionId, int userId, boolean active) {
1190 obtainMessage(MSG_SESSION_ACTIVE_CHANGED, sessionId, userId, active).sendToTarget();
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001191 }
1192
1193 private void notifySessionProgressChanged(int sessionId, int userId, float progress) {
1194 obtainMessage(MSG_SESSION_PROGRESS_CHANGED, sessionId, userId, progress).sendToTarget();
1195 }
1196
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001197 public void notifySessionFinished(int sessionId, int userId, boolean success) {
1198 obtainMessage(MSG_SESSION_FINISHED, sessionId, userId, success).sendToTarget();
1199 }
Jeff Sharkeya1031142014-07-12 18:09:46 -07001200 }
1201
1202 void dump(IndentingPrintWriter pw) {
Jeff Sharkeya1031142014-07-12 18:09:46 -07001203 synchronized (mSessions) {
Jeff Sharkey9a445772014-07-16 11:32:08 -07001204 pw.println("Active install sessions:");
1205 pw.increaseIndent();
1206 int N = mSessions.size();
Jeff Sharkeya1031142014-07-12 18:09:46 -07001207 for (int i = 0; i < N; i++) {
1208 final PackageInstallerSession session = mSessions.valueAt(i);
1209 session.dump(pw);
1210 pw.println();
1211 }
Jeff Sharkey9a445772014-07-16 11:32:08 -07001212 pw.println();
1213 pw.decreaseIndent();
1214
1215 pw.println("Historical install sessions:");
1216 pw.increaseIndent();
1217 N = mHistoricalSessions.size();
1218 for (int i = 0; i < N; i++) {
1219 final PackageInstallerSession session = mHistoricalSessions.valueAt(i);
1220 session.dump(pw);
1221 pw.println();
1222 }
1223 pw.println();
1224 pw.decreaseIndent();
Jeff Sharkey742e7902014-08-16 19:09:13 -07001225
1226 pw.println("Legacy install sessions:");
1227 pw.increaseIndent();
1228 pw.println(mLegacySessions.toString());
1229 pw.decreaseIndent();
Jeff Sharkeya1031142014-07-12 18:09:46 -07001230 }
Jeff Sharkeybb580672014-07-10 12:10:25 -07001231 }
1232
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001233 class InternalCallback {
Jeff Sharkeyec9bad22014-09-05 09:45:20 -07001234 public void onSessionBadgingChanged(PackageInstallerSession session) {
1235 mCallbacks.notifySessionBadgingChanged(session.sessionId, session.userId);
1236 writeSessionsAsync();
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001237 }
1238
Jeff Sharkeybc7bce32014-09-05 15:53:05 -07001239 public void onSessionActiveChanged(PackageInstallerSession session, boolean active) {
1240 mCallbacks.notifySessionActiveChanged(session.sessionId, session.userId, active);
Jeff Sharkey742e7902014-08-16 19:09:13 -07001241 }
1242
Jeff Sharkeyec9bad22014-09-05 09:45:20 -07001243 public void onSessionProgressChanged(PackageInstallerSession session, float progress) {
1244 mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId, progress);
1245 }
1246
Jeff Sharkeycbf47912014-09-12 09:55:32 -07001247 public void onSessionFinished(final PackageInstallerSession session, boolean success) {
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001248 mCallbacks.notifySessionFinished(session.sessionId, session.userId, success);
Jeff Sharkeycbf47912014-09-12 09:55:32 -07001249
1250 mInstallHandler.post(new Runnable() {
1251 @Override
1252 public void run() {
1253 synchronized (mSessions) {
1254 mSessions.remove(session.sessionId);
1255 mHistoricalSessions.put(session.sessionId, session);
Jeff Sharkey02bd7842014-10-06 15:14:27 -07001256
1257 final File appIconFile = buildAppIconFile(session.sessionId);
1258 if (appIconFile.exists()) {
1259 appIconFile.delete();
1260 }
1261
Jeff Sharkeycbf47912014-09-12 09:55:32 -07001262 writeSessionsLocked();
1263 }
1264 }
1265 });
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -07001266 }
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -07001267
Jeff Sharkey77d218e2014-09-06 12:20:37 -07001268 public void onSessionPrepared(PackageInstallerSession session) {
1269 // We prepared the destination to write into; we want to persist
1270 // this, but it's not critical enough to block for.
1271 writeSessionsAsync();
1272 }
1273
Jeff Sharkeycbf47912014-09-12 09:55:32 -07001274 public void onSessionSealedBlocking(PackageInstallerSession session) {
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -07001275 // It's very important that we block until we've recorded the
1276 // session as being sealed, since we never want to allow mutation
1277 // after sealing.
Jeff Sharkeycbf47912014-09-12 09:55:32 -07001278 synchronized (mSessions) {
1279 writeSessionsLocked();
1280 }
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -07001281 }
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -07001282 }
1283}