blob: 83af0173c514b2fecaa0784ca6ad6fd6264c64b3 [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
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700185 @GuardedBy("mSessions")
186 private final SparseArray<PackageInstallerSession> mSessions = new SparseArray<>();
187
Jeff Sharkey9a445772014-07-16 11:32:08 -0700188 /** Historical sessions kept around for debugging purposes */
189 @GuardedBy("mSessions")
190 private final SparseArray<PackageInstallerSession> mHistoricalSessions = new SparseArray<>();
191
Jeff Sharkey742e7902014-08-16 19:09:13 -0700192 /** Sessions allocated to legacy users */
193 @GuardedBy("mSessions")
194 private final SparseBooleanArray mLegacySessions = new SparseBooleanArray();
195
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700196 private static final FilenameFilter sStageFilter = new FilenameFilter() {
197 @Override
198 public boolean accept(File dir, String name) {
Jeff Sharkey742e7902014-08-16 19:09:13 -0700199 return isStageName(name);
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700200 }
201 };
202
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -0700203 public PackageInstallerService(Context context, PackageManagerService pm) {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700204 mContext = context;
205 mPm = pm;
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700206
207 mInstallThread = new HandlerThread(TAG);
208 mInstallThread.start();
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700209
Jeff Sharkeycbf47912014-09-12 09:55:32 -0700210 mInstallHandler = new Handler(mInstallThread.getLooper());
211
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700212 mCallbacks = new Callbacks(mInstallThread.getLooper());
213
214 mSessionsFile = new AtomicFile(
Jeff Sharkey8212ae02016-02-10 14:46:43 -0700215 new File(Environment.getDataSystemDirectory(), "install_sessions.xml"));
216 mSessionsDir = new File(Environment.getDataSystemDirectory(), "install_sessions");
Jeff Sharkey02bd7842014-10-06 15:14:27 -0700217 mSessionsDir.mkdirs();
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700218
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700219 synchronized (mSessions) {
220 readSessionsLocked();
221
Todd Kennedy2699f062015-11-20 13:07:17 -0800222 reconcileStagesLocked(StorageManager.UUID_PRIVATE_INTERNAL, false /*isEphemeral*/);
223 reconcileStagesLocked(StorageManager.UUID_PRIVATE_INTERNAL, true /*isEphemeral*/);
Jeff Sharkey6dce4962015-07-03 18:08:41 -0700224
Jeff Sharkey54d42be2015-07-20 16:36:55 -0700225 final ArraySet<File> unclaimedIcons = newArraySet(
Jeff Sharkey02bd7842014-10-06 15:14:27 -0700226 mSessionsDir.listFiles());
Jeff Sharkey742e7902014-08-16 19:09:13 -0700227
Jeff Sharkey02bd7842014-10-06 15:14:27 -0700228 // Ignore stages and icons claimed by active sessions
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700229 for (int i = 0; i < mSessions.size(); i++) {
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700230 final PackageInstallerSession session = mSessions.valueAt(i);
Jeff Sharkey02bd7842014-10-06 15:14:27 -0700231 unclaimedIcons.remove(buildAppIconFile(session.sessionId));
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700232 }
Jeff Sharkey742e7902014-08-16 19:09:13 -0700233
Jeff Sharkey02bd7842014-10-06 15:14:27 -0700234 // Clean up orphaned icons
235 for (File icon : unclaimedIcons) {
236 Slog.w(TAG, "Deleting orphan icon " + icon);
237 icon.delete();
238 }
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700239 }
240 }
241
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -0700242 public void systemReady() {
243 mAppOps = mContext.getSystemService(AppOpsManager.class);
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -0700244 }
245
Todd Kennedy2699f062015-11-20 13:07:17 -0800246 private void reconcileStagesLocked(String volumeUuid, boolean isEphemeral) {
247 final File stagingDir = buildStagingDir(volumeUuid, isEphemeral);
Jeff Sharkey54d42be2015-07-20 16:36:55 -0700248 final ArraySet<File> unclaimedStages = newArraySet(
Jeff Sharkey6dce4962015-07-03 18:08:41 -0700249 stagingDir.listFiles(sStageFilter));
250
251 // Ignore stages claimed by active sessions
252 for (int i = 0; i < mSessions.size(); i++) {
253 final PackageInstallerSession session = mSessions.valueAt(i);
254 unclaimedStages.remove(session.stageDir);
255 }
256
257 // Clean up orphaned staging directories
258 for (File stage : unclaimedStages) {
259 Slog.w(TAG, "Deleting orphan stage " + stage);
260 synchronized (mPm.mInstallLock) {
Jeff Sharkeyfdeeeea2016-01-11 17:34:24 -0700261 mPm.removeCodePathLI(stage);
Jeff Sharkey6dce4962015-07-03 18:08:41 -0700262 }
263 }
264 }
265
266 public void onPrivateVolumeMounted(String volumeUuid) {
267 synchronized (mSessions) {
Todd Kennedy2699f062015-11-20 13:07:17 -0800268 reconcileStagesLocked(volumeUuid, false /*isEphemeral*/);
Jeff Sharkey6dce4962015-07-03 18:08:41 -0700269 }
270 }
271
Jeff Sharkey742e7902014-08-16 19:09:13 -0700272 public void onSecureContainersAvailable() {
273 synchronized (mSessions) {
274 final ArraySet<String> unclaimed = new ArraySet<>();
275 for (String cid : PackageHelper.getSecureContainerList()) {
276 if (isStageName(cid)) {
277 unclaimed.add(cid);
278 }
279 }
280
281 // Ignore stages claimed by active sessions
282 for (int i = 0; i < mSessions.size(); i++) {
283 final PackageInstallerSession session = mSessions.valueAt(i);
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700284 final String cid = session.stageCid;
Jeff Sharkey742e7902014-08-16 19:09:13 -0700285
286 if (unclaimed.remove(cid)) {
287 // Claimed by active session, mount it
288 PackageHelper.mountSdDir(cid, PackageManagerService.getEncryptKey(),
289 Process.SYSTEM_UID);
290 }
291 }
292
293 // Clean up orphaned staging containers
294 for (String cid : unclaimed) {
295 Slog.w(TAG, "Deleting orphan container " + cid);
296 PackageHelper.destroySdDir(cid);
297 }
298 }
299 }
300
301 public static boolean isStageName(String name) {
302 final boolean isFile = name.startsWith("vmdl") && name.endsWith(".tmp");
303 final boolean isContainer = name.startsWith("smdl") && name.endsWith(".tmp");
304 final boolean isLegacyContainer = name.startsWith("smdl2tmp");
305 return isFile || isContainer || isLegacyContainer;
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700306 }
307
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700308 @Deprecated
Todd Kennedy2699f062015-11-20 13:07:17 -0800309 public File allocateStageDirLegacy(String volumeUuid, boolean isEphemeral) throws IOException {
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700310 synchronized (mSessions) {
311 try {
312 final int sessionId = allocateSessionIdLocked();
Jeff Sharkey742e7902014-08-16 19:09:13 -0700313 mLegacySessions.put(sessionId, true);
Todd Kennedy2699f062015-11-20 13:07:17 -0800314 final File stageDir = buildStageDir(volumeUuid, sessionId, isEphemeral);
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -0700315 prepareStageDir(stageDir);
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700316 return stageDir;
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700317 } catch (IllegalStateException e) {
318 throw new IOException(e);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700319 }
320 }
321 }
322
Jeff Sharkey742e7902014-08-16 19:09:13 -0700323 @Deprecated
324 public String allocateExternalStageCidLegacy() {
325 synchronized (mSessions) {
326 final int sessionId = allocateSessionIdLocked();
327 mLegacySessions.put(sessionId, true);
328 return "smdl" + sessionId + ".tmp";
329 }
330 }
331
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700332 private void readSessionsLocked() {
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700333 if (LOGD) Slog.v(TAG, "readSessionsLocked()");
334
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700335 mSessions.clear();
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700336
337 FileInputStream fis = null;
338 try {
339 fis = mSessionsFile.openRead();
340 final XmlPullParser in = Xml.newPullParser();
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +0100341 in.setInput(fis, StandardCharsets.UTF_8.name());
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700342
343 int type;
344 while ((type = in.next()) != END_DOCUMENT) {
345 if (type == START_TAG) {
346 final String tag = in.getName();
347 if (TAG_SESSION.equals(tag)) {
348 final PackageInstallerSession session = readSessionLocked(in);
349 final long age = System.currentTimeMillis() - session.createdMillis;
350
351 final boolean valid;
352 if (age >= MAX_AGE_MILLIS) {
353 Slog.w(TAG, "Abandoning old session first created at "
354 + session.createdMillis);
355 valid = false;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700356 } else {
357 valid = true;
358 }
359
360 if (valid) {
361 mSessions.put(session.sessionId, session);
362 } else {
363 // Since this is early during boot we don't send
364 // any observer events about the session, but we
365 // keep details around for dumpsys.
366 mHistoricalSessions.put(session.sessionId, session);
367 }
368 }
369 }
370 }
371 } catch (FileNotFoundException e) {
372 // Missing sessions are okay, probably first boot
Svet Ganov7121e182015-07-13 22:38:12 -0700373 } catch (IOException | XmlPullParserException e) {
Dianne Hackborn8d051722014-10-01 14:59:58 -0700374 Slog.wtf(TAG, "Failed reading install sessions", e);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700375 } finally {
376 IoUtils.closeQuietly(fis);
377 }
378 }
379
Svet Ganov7121e182015-07-13 22:38:12 -0700380 private PackageInstallerSession readSessionLocked(XmlPullParser in) throws IOException,
381 XmlPullParserException {
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700382 final int sessionId = readIntAttribute(in, ATTR_SESSION_ID);
383 final int userId = readIntAttribute(in, ATTR_USER_ID);
384 final String installerPackageName = readStringAttribute(in, ATTR_INSTALLER_PACKAGE_NAME);
Jeff Sharkeycd654482016-01-08 17:42:11 -0700385 final int installerUid = readIntAttribute(in, ATTR_INSTALLER_UID, mPm.getPackageUid(
386 installerPackageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId));
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700387 final long createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS);
Jeff Sharkey742e7902014-08-16 19:09:13 -0700388 final String stageDirRaw = readStringAttribute(in, ATTR_SESSION_STAGE_DIR);
389 final File stageDir = (stageDirRaw != null) ? new File(stageDirRaw) : null;
390 final String stageCid = readStringAttribute(in, ATTR_SESSION_STAGE_CID);
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700391 final boolean prepared = readBooleanAttribute(in, ATTR_PREPARED, true);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700392 final boolean sealed = readBooleanAttribute(in, ATTR_SEALED);
393
Jeff Sharkeya0907432014-08-15 10:23:11 -0700394 final SessionParams params = new SessionParams(
395 SessionParams.MODE_INVALID);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700396 params.mode = readIntAttribute(in, ATTR_MODE);
397 params.installFlags = readIntAttribute(in, ATTR_INSTALL_FLAGS);
398 params.installLocation = readIntAttribute(in, ATTR_INSTALL_LOCATION);
399 params.sizeBytes = readLongAttribute(in, ATTR_SIZE_BYTES);
400 params.appPackageName = readStringAttribute(in, ATTR_APP_PACKAGE_NAME);
401 params.appIcon = readBitmapAttribute(in, ATTR_APP_ICON);
402 params.appLabel = readStringAttribute(in, ATTR_APP_LABEL);
403 params.originatingUri = readUriAttribute(in, ATTR_ORIGINATING_URI);
Todd Kennedya1d12cf2015-09-29 15:43:00 -0700404 params.originatingUid =
405 readIntAttribute(in, ATTR_ORIGINATING_UID, SessionParams.UID_UNKNOWN);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700406 params.referrerUri = readUriAttribute(in, ATTR_REFERRER_URI);
407 params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE);
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -0700408 params.volumeUuid = readStringAttribute(in, ATTR_VOLUME_UUID);
Svet Ganov7121e182015-07-13 22:38:12 -0700409 params.grantedRuntimePermissions = readGrantedRuntimePermissions(in);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700410
Jeff Sharkey02bd7842014-10-06 15:14:27 -0700411 final File appIconFile = buildAppIconFile(sessionId);
412 if (appIconFile.exists()) {
413 params.appIcon = BitmapFactory.decodeFile(appIconFile.getAbsolutePath());
414 params.appIconLastModified = appIconFile.lastModified();
415 }
416
Jeff Sharkeya0907432014-08-15 10:23:11 -0700417 return new PackageInstallerSession(mInternalCallback, mContext, mPm,
Jeff Sharkeye9808042014-09-11 21:15:37 -0700418 mInstallThread.getLooper(), sessionId, userId, installerPackageName, installerUid,
419 params, createdMillis, stageDir, stageCid, prepared, sealed);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700420 }
421
422 private void writeSessionsLocked() {
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700423 if (LOGD) Slog.v(TAG, "writeSessionsLocked()");
424
425 FileOutputStream fos = null;
426 try {
427 fos = mSessionsFile.startWrite();
428
429 XmlSerializer out = new FastXmlSerializer();
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +0100430 out.setOutput(fos, StandardCharsets.UTF_8.name());
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700431 out.startDocument(null, true);
432 out.startTag(null, TAG_SESSIONS);
433 final int size = mSessions.size();
434 for (int i = 0; i < size; i++) {
435 final PackageInstallerSession session = mSessions.valueAt(i);
436 writeSessionLocked(out, session);
437 }
438 out.endTag(null, TAG_SESSIONS);
439 out.endDocument();
440
441 mSessionsFile.finishWrite(fos);
442 } catch (IOException e) {
443 if (fos != null) {
444 mSessionsFile.failWrite(fos);
445 }
446 }
447 }
448
449 private void writeSessionLocked(XmlSerializer out, PackageInstallerSession session)
450 throws IOException {
Jeff Sharkeya0907432014-08-15 10:23:11 -0700451 final SessionParams params = session.params;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700452
453 out.startTag(null, TAG_SESSION);
454
455 writeIntAttribute(out, ATTR_SESSION_ID, session.sessionId);
456 writeIntAttribute(out, ATTR_USER_ID, session.userId);
457 writeStringAttribute(out, ATTR_INSTALLER_PACKAGE_NAME,
458 session.installerPackageName);
Jeff Sharkeye9808042014-09-11 21:15:37 -0700459 writeIntAttribute(out, ATTR_INSTALLER_UID, session.installerUid);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700460 writeLongAttribute(out, ATTR_CREATED_MILLIS, session.createdMillis);
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700461 if (session.stageDir != null) {
Jeff Sharkey742e7902014-08-16 19:09:13 -0700462 writeStringAttribute(out, ATTR_SESSION_STAGE_DIR,
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700463 session.stageDir.getAbsolutePath());
Jeff Sharkey742e7902014-08-16 19:09:13 -0700464 }
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700465 if (session.stageCid != null) {
466 writeStringAttribute(out, ATTR_SESSION_STAGE_CID, session.stageCid);
Jeff Sharkey742e7902014-08-16 19:09:13 -0700467 }
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700468 writeBooleanAttribute(out, ATTR_PREPARED, session.isPrepared());
Jeff Sharkey742e7902014-08-16 19:09:13 -0700469 writeBooleanAttribute(out, ATTR_SEALED, session.isSealed());
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700470
471 writeIntAttribute(out, ATTR_MODE, params.mode);
472 writeIntAttribute(out, ATTR_INSTALL_FLAGS, params.installFlags);
473 writeIntAttribute(out, ATTR_INSTALL_LOCATION, params.installLocation);
474 writeLongAttribute(out, ATTR_SIZE_BYTES, params.sizeBytes);
475 writeStringAttribute(out, ATTR_APP_PACKAGE_NAME, params.appPackageName);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700476 writeStringAttribute(out, ATTR_APP_LABEL, params.appLabel);
477 writeUriAttribute(out, ATTR_ORIGINATING_URI, params.originatingUri);
Todd Kennedya1d12cf2015-09-29 15:43:00 -0700478 writeIntAttribute(out, ATTR_ORIGINATING_UID, params.originatingUid);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700479 writeUriAttribute(out, ATTR_REFERRER_URI, params.referrerUri);
480 writeStringAttribute(out, ATTR_ABI_OVERRIDE, params.abiOverride);
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -0700481 writeStringAttribute(out, ATTR_VOLUME_UUID, params.volumeUuid);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700482
Jeff Sharkey02bd7842014-10-06 15:14:27 -0700483 // Persist app icon if changed since last written
484 final File appIconFile = buildAppIconFile(session.sessionId);
485 if (params.appIcon == null && appIconFile.exists()) {
486 appIconFile.delete();
487 } else if (params.appIcon != null
488 && appIconFile.lastModified() != params.appIconLastModified) {
489 if (LOGD) Slog.w(TAG, "Writing changed icon " + appIconFile);
490 FileOutputStream os = null;
491 try {
492 os = new FileOutputStream(appIconFile);
493 params.appIcon.compress(CompressFormat.PNG, 90, os);
494 } catch (IOException e) {
495 Slog.w(TAG, "Failed to write icon " + appIconFile + ": " + e.getMessage());
496 } finally {
497 IoUtils.closeQuietly(os);
498 }
499
500 params.appIconLastModified = appIconFile.lastModified();
501 }
502
Svet Ganov7121e182015-07-13 22:38:12 -0700503 writeGrantedRuntimePermissions(out, params.grantedRuntimePermissions);
504
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700505 out.endTag(null, TAG_SESSION);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700506 }
507
Svet Ganov7121e182015-07-13 22:38:12 -0700508 private static void writeGrantedRuntimePermissions(XmlSerializer out,
509 String[] grantedRuntimePermissions) throws IOException {
510 if (grantedRuntimePermissions != null) {
511 for (String permission : grantedRuntimePermissions) {
512 out.startTag(null, TAG_GRANTED_RUNTIME_PERMISSION);
513 writeStringAttribute(out, ATTR_NAME, permission);
514 out.endTag(null, TAG_GRANTED_RUNTIME_PERMISSION);
515 }
516 }
517 }
518
519 private static String[] readGrantedRuntimePermissions(XmlPullParser in)
520 throws IOException, XmlPullParserException {
521 List<String> permissions = null;
522
523 final int outerDepth = in.getDepth();
524 int type;
525 while ((type = in.next()) != XmlPullParser.END_DOCUMENT
526 && (type != XmlPullParser.END_TAG || in.getDepth() > outerDepth)) {
527 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
528 continue;
529 }
530 if (TAG_GRANTED_RUNTIME_PERMISSION.equals(in.getName())) {
531 String permission = readStringAttribute(in, ATTR_NAME);
532 if (permissions == null) {
533 permissions = new ArrayList<>();
534 }
535 permissions.add(permission);
536 }
537 }
538
539 if (permissions == null) {
540 return null;
541 }
542
543 String[] permissionsArray = new String[permissions.size()];
544 permissions.toArray(permissionsArray);
545 return permissionsArray;
546 }
547
Jeff Sharkey02bd7842014-10-06 15:14:27 -0700548 private File buildAppIconFile(int sessionId) {
549 return new File(mSessionsDir, "app_icon." + sessionId + ".png");
550 }
551
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700552 private void writeSessionsAsync() {
553 IoThread.getHandler().post(new Runnable() {
554 @Override
555 public void run() {
556 synchronized (mSessions) {
557 writeSessionsLocked();
558 }
559 }
560 });
561 }
562
563 @Override
Jeff Sharkeya0907432014-08-15 10:23:11 -0700564 public int createSession(SessionParams params, String installerPackageName, int userId) {
Jeff Sharkey742e7902014-08-16 19:09:13 -0700565 try {
566 return createSessionInternal(params, installerPackageName, userId);
567 } catch (IOException e) {
568 throw ExceptionUtils.wrap(e);
569 }
570 }
571
572 private int createSessionInternal(SessionParams params, String installerPackageName, int userId)
573 throws IOException {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700574 final int callingUid = Binder.getCallingUid();
Jeff Sharkeye9808042014-09-11 21:15:37 -0700575 mPm.enforceCrossUserPermission(callingUid, userId, true, true, "createSession");
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700576
Jeff Sharkeye9808042014-09-11 21:15:37 -0700577 if (mPm.isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700578 throw new SecurityException("User restriction prevents installing");
579 }
580
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700581 if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {
Jeff Sharkeye9808042014-09-11 21:15:37 -0700582 params.installFlags |= PackageManager.INSTALL_FROM_ADB;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700583
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700584 } else {
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700585 mAppOps.checkPackage(callingUid, installerPackageName);
586
Jeff Sharkeye9808042014-09-11 21:15:37 -0700587 params.installFlags &= ~PackageManager.INSTALL_FROM_ADB;
588 params.installFlags &= ~PackageManager.INSTALL_ALL_USERS;
589 params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700590 }
591
Svetoslav805b63e2015-04-09 17:28:54 -0700592 // Only system components can circumvent runtime permissions when installing.
593 if ((params.installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0
594 && mContext.checkCallingOrSelfPermission(Manifest.permission
595 .INSTALL_GRANT_RUNTIME_PERMISSIONS) == PackageManager.PERMISSION_DENIED) {
596 throw new SecurityException("You need the "
597 + "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission "
598 + "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag");
599 }
600
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700601 // Defensively resize giant app icons
602 if (params.appIcon != null) {
603 final ActivityManager am = (ActivityManager) mContext.getSystemService(
604 Context.ACTIVITY_SERVICE);
605 final int iconSize = am.getLauncherLargeIconSize();
606 if ((params.appIcon.getWidth() > iconSize * 2)
607 || (params.appIcon.getHeight() > iconSize * 2)) {
608 params.appIcon = Bitmap.createScaledBitmap(params.appIcon, iconSize, iconSize,
609 true);
610 }
611 }
612
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -0700613 switch (params.mode) {
614 case SessionParams.MODE_FULL_INSTALL:
615 case SessionParams.MODE_INHERIT_EXISTING:
616 break;
617 default:
618 throw new IllegalArgumentException("Invalid install mode: " + params.mode);
619 }
620
621 // If caller requested explicit location, sanity check it, otherwise
622 // resolve the best internal or adopted location.
623 if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
624 if (!PackageHelper.fitsOnInternal(mContext, params.sizeBytes)) {
625 throw new IOException("No suitable internal storage available");
626 }
627
628 } else if ((params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
629 if (!PackageHelper.fitsOnExternal(mContext, params.sizeBytes)) {
630 throw new IOException("No suitable external storage available");
631 }
632
Jeff Sharkeyab234092015-06-09 21:42:22 -0700633 } else if ((params.installFlags & PackageManager.INSTALL_FORCE_VOLUME_UUID) != 0) {
634 // For now, installs to adopted media are treated as internal from
635 // an install flag point-of-view.
636 params.setInstallFlagsInternal();
637
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -0700638 } else {
639 // For now, installs to adopted media are treated as internal from
640 // an install flag point-of-view.
641 params.setInstallFlagsInternal();
642
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700643 // Resolve best location for install, based on combination of
644 // requested install flags, delta size, and manifest settings.
Robin Leee812d902014-08-21 16:51:18 +0100645 final long ident = Binder.clearCallingIdentity();
646 try {
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -0700647 params.volumeUuid = PackageHelper.resolveInstallVolume(mContext,
648 params.appPackageName, params.installLocation, params.sizeBytes);
Robin Leee812d902014-08-21 16:51:18 +0100649 } finally {
650 Binder.restoreCallingIdentity(ident);
651 }
Jeff Sharkeya1031142014-07-12 18:09:46 -0700652 }
653
654 final int sessionId;
655 final PackageInstallerSession session;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700656 synchronized (mSessions) {
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700657 // Sanity check that installer isn't going crazy
Jeff Sharkeyf174c6e2014-08-05 10:42:27 -0700658 final int activeCount = getSessionCount(mSessions, callingUid);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700659 if (activeCount >= MAX_ACTIVE_SESSIONS) {
Jeff Sharkeyf174c6e2014-08-05 10:42:27 -0700660 throw new IllegalStateException(
661 "Too many active sessions for UID " + callingUid);
662 }
663 final int historicalCount = getSessionCount(mHistoricalSessions, callingUid);
664 if (historicalCount >= MAX_HISTORICAL_SESSIONS) {
665 throw new IllegalStateException(
666 "Too many historical sessions for UID " + callingUid);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700667 }
668
Jeff Sharkey742e7902014-08-16 19:09:13 -0700669 final long createdMillis = System.currentTimeMillis();
Jeff Sharkeya1031142014-07-12 18:09:46 -0700670 sessionId = allocateSessionIdLocked();
671
Jeff Sharkey742e7902014-08-16 19:09:13 -0700672 // We're staging to exactly one location
673 File stageDir = null;
674 String stageCid = null;
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700675 if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
Todd Kennedy2699f062015-11-20 13:07:17 -0800676 final boolean isEphemeral =
677 (params.installFlags & PackageManager.INSTALL_EPHEMERAL) != 0;
678 stageDir = buildStageDir(params.volumeUuid, sessionId, isEphemeral);
Jeff Sharkey742e7902014-08-16 19:09:13 -0700679 } else {
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700680 stageCid = buildExternalStageCid(sessionId);
Jeff Sharkey742e7902014-08-16 19:09:13 -0700681 }
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700682
Jeff Sharkeya0907432014-08-15 10:23:11 -0700683 session = new PackageInstallerSession(mInternalCallback, mContext, mPm,
Jeff Sharkeye9808042014-09-11 21:15:37 -0700684 mInstallThread.getLooper(), sessionId, userId, installerPackageName, callingUid,
685 params, createdMillis, stageDir, stageCid, false, false);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700686 mSessions.put(sessionId, session);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700687 }
Jeff Sharkeya1031142014-07-12 18:09:46 -0700688
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700689 mCallbacks.notifySessionCreated(session.sessionId, session.userId);
Jeff Sharkeya1031142014-07-12 18:09:46 -0700690 writeSessionsAsync();
691 return sessionId;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700692 }
693
Jeff Sharkey381d94b2014-08-24 14:45:56 -0700694 @Override
Jeff Sharkeyec9bad22014-09-05 09:45:20 -0700695 public void updateSessionAppIcon(int sessionId, Bitmap appIcon) {
696 synchronized (mSessions) {
697 final PackageInstallerSession session = mSessions.get(sessionId);
698 if (session == null || !isCallingUidOwner(session)) {
699 throw new SecurityException("Caller has no access to session " + sessionId);
700 }
Jeff Sharkey02bd7842014-10-06 15:14:27 -0700701
702 // Defensively resize giant app icons
703 if (appIcon != null) {
704 final ActivityManager am = (ActivityManager) mContext.getSystemService(
705 Context.ACTIVITY_SERVICE);
706 final int iconSize = am.getLauncherLargeIconSize();
707 if ((appIcon.getWidth() > iconSize * 2)
708 || (appIcon.getHeight() > iconSize * 2)) {
709 appIcon = Bitmap.createScaledBitmap(appIcon, iconSize, iconSize, true);
710 }
711 }
712
Jeff Sharkeyec9bad22014-09-05 09:45:20 -0700713 session.params.appIcon = appIcon;
Jeff Sharkey02bd7842014-10-06 15:14:27 -0700714 session.params.appIconLastModified = -1;
715
Jeff Sharkeyec9bad22014-09-05 09:45:20 -0700716 mInternalCallback.onSessionBadgingChanged(session);
717 }
718 }
719
720 @Override
721 public void updateSessionAppLabel(int sessionId, String appLabel) {
722 synchronized (mSessions) {
723 final PackageInstallerSession session = mSessions.get(sessionId);
724 if (session == null || !isCallingUidOwner(session)) {
725 throw new SecurityException("Caller has no access to session " + sessionId);
726 }
727 session.params.appLabel = appLabel;
728 mInternalCallback.onSessionBadgingChanged(session);
729 }
730 }
731
732 @Override
Jeff Sharkey381d94b2014-08-24 14:45:56 -0700733 public void abandonSession(int sessionId) {
734 synchronized (mSessions) {
735 final PackageInstallerSession session = mSessions.get(sessionId);
736 if (session == null || !isCallingUidOwner(session)) {
737 throw new SecurityException("Caller has no access to session " + sessionId);
738 }
739 session.abandon();
740 }
741 }
742
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700743 @Override
744 public IPackageInstallerSession openSession(int sessionId) {
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700745 try {
746 return openSessionInternal(sessionId);
747 } catch (IOException e) {
748 throw ExceptionUtils.wrap(e);
749 }
750 }
751
752 private IPackageInstallerSession openSessionInternal(int sessionId) throws IOException {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700753 synchronized (mSessions) {
754 final PackageInstallerSession session = mSessions.get(sessionId);
Jeff Sharkey381d94b2014-08-24 14:45:56 -0700755 if (session == null || !isCallingUidOwner(session)) {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700756 throw new SecurityException("Caller has no access to session " + sessionId);
757 }
Jeff Sharkey742e7902014-08-16 19:09:13 -0700758 session.open();
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700759 return session;
760 }
761 }
762
763 private int allocateSessionIdLocked() {
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700764 int n = 0;
765 int sessionId;
766 do {
Jeff Sharkeyf174c6e2014-08-05 10:42:27 -0700767 sessionId = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
Jeff Sharkey742e7902014-08-16 19:09:13 -0700768 if (mSessions.get(sessionId) == null && mHistoricalSessions.get(sessionId) == null
769 && !mLegacySessions.get(sessionId, false)) {
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700770 return sessionId;
771 }
772 } while (n++ < 32);
773
774 throw new IllegalStateException("Failed to allocate session ID");
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700775 }
776
Todd Kennedy2699f062015-11-20 13:07:17 -0800777 private File buildStagingDir(String volumeUuid, boolean isEphemeral) {
778 if (isEphemeral) {
779 return Environment.getDataAppEphemeralDirectory(volumeUuid);
780 }
Jeff Sharkey6dce4962015-07-03 18:08:41 -0700781 return Environment.getDataAppDirectory(volumeUuid);
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700782 }
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700783
Todd Kennedy2699f062015-11-20 13:07:17 -0800784 private File buildStageDir(String volumeUuid, int sessionId, boolean isEphemeral) {
785 final File stagingDir = buildStagingDir(volumeUuid, isEphemeral);
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -0700786 return new File(stagingDir, "vmdl" + sessionId + ".tmp");
787 }
788
789 static void prepareStageDir(File stageDir) throws IOException {
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700790 if (stageDir.exists()) {
791 throw new IOException("Session dir already exists: " + stageDir);
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700792 }
793
794 try {
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700795 Os.mkdir(stageDir.getAbsolutePath(), 0755);
796 Os.chmod(stageDir.getAbsolutePath(), 0755);
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700797 } catch (ErrnoException e) {
798 // This purposefully throws if directory already exists
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700799 throw new IOException("Failed to prepare session dir: " + stageDir, e);
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700800 }
801
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700802 if (!SELinux.restorecon(stageDir)) {
803 throw new IOException("Failed to restorecon session dir: " + stageDir);
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700804 }
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700805 }
806
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700807 private String buildExternalStageCid(int sessionId) {
808 return "smdl" + sessionId + ".tmp";
809 }
Jeff Sharkey742e7902014-08-16 19:09:13 -0700810
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700811 static void prepareExternalStageCid(String stageCid, long sizeBytes) throws IOException {
812 if (PackageHelper.createSdDir(sizeBytes, stageCid, PackageManagerService.getEncryptKey(),
Jeff Sharkey742e7902014-08-16 19:09:13 -0700813 Process.SYSTEM_UID, true) == null) {
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700814 throw new IOException("Failed to create session cid: " + stageCid);
Jeff Sharkey742e7902014-08-16 19:09:13 -0700815 }
Jeff Sharkey742e7902014-08-16 19:09:13 -0700816 }
817
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700818 @Override
Jeff Sharkeya0907432014-08-15 10:23:11 -0700819 public SessionInfo getSessionInfo(int sessionId) {
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700820 synchronized (mSessions) {
821 final PackageInstallerSession session = mSessions.get(sessionId);
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700822 return session != null ? session.generateInfo() : null;
823 }
824 }
825
826 @Override
Jeff Sharkey97d47ed2014-10-15 09:19:47 -0700827 public ParceledListSlice<SessionInfo> getAllSessions(int userId) {
Amith Yamasani8cd28b52014-06-08 17:54:27 -0700828 mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "getAllSessions");
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700829
Jeff Sharkeya0907432014-08-15 10:23:11 -0700830 final List<SessionInfo> result = new ArrayList<>();
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700831 synchronized (mSessions) {
832 for (int i = 0; i < mSessions.size(); i++) {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700833 final PackageInstallerSession session = mSessions.valueAt(i);
Jeff Sharkeybb580672014-07-10 12:10:25 -0700834 if (session.userId == userId) {
835 result.add(session.generateInfo());
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700836 }
837 }
838 }
Jeff Sharkey97d47ed2014-10-15 09:19:47 -0700839 return new ParceledListSlice<>(result);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700840 }
841
842 @Override
Jeff Sharkey97d47ed2014-10-15 09:19:47 -0700843 public ParceledListSlice<SessionInfo> getMySessions(String installerPackageName, int userId) {
Amith Yamasani8cd28b52014-06-08 17:54:27 -0700844 mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "getMySessions");
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700845 mAppOps.checkPackage(Binder.getCallingUid(), installerPackageName);
846
Jeff Sharkeya0907432014-08-15 10:23:11 -0700847 final List<SessionInfo> result = new ArrayList<>();
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700848 synchronized (mSessions) {
849 for (int i = 0; i < mSessions.size(); i++) {
850 final PackageInstallerSession session = mSessions.valueAt(i);
851 if (Objects.equals(session.installerPackageName, installerPackageName)
852 && session.userId == userId) {
853 result.add(session.generateInfo());
854 }
855 }
856 }
Jeff Sharkey97d47ed2014-10-15 09:19:47 -0700857 return new ParceledListSlice<>(result);
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700858 }
859
860 @Override
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000861 public void uninstall(String packageName, String callerPackageName, int flags,
862 IntentSender statusReceiver, int userId) {
863 final int callingUid = Binder.getCallingUid();
864 mPm.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall");
Sudheer Shanka29283372016-04-04 20:56:27 -0700865 boolean allowSilentUninstall = true;
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000866 if ((callingUid != Process.SHELL_UID) && (callingUid != Process.ROOT_UID)) {
867 mAppOps.checkPackage(callingUid, callerPackageName);
Sudheer Shanka29283372016-04-04 20:56:27 -0700868 final String installerPackageName = mPm.getInstallerPackageName(packageName);
Sudheer Shanka8c57aea2016-04-20 16:47:50 -0700869 allowSilentUninstall = mPm.isOrphaned(packageName) ||
870 (installerPackageName != null
871 && installerPackageName.equals(callerPackageName));
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000872 }
873
Makoto Onukic8a5a552015-11-19 14:29:12 -0800874 // Check whether the caller is device owner, in which case we do it silently.
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000875 DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(
876 Context.DEVICE_POLICY_SERVICE);
Makoto Onukic8a5a552015-11-19 14:29:12 -0800877 boolean isDeviceOwner = (dpm != null) && dpm.isDeviceOwnerAppOnCallingUser(
878 callerPackageName);
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700879
Jeff Sharkeya0907432014-08-15 10:23:11 -0700880 final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext,
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000881 statusReceiver, packageName, isDeviceOwner, userId);
Sudheer Shanka29283372016-04-04 20:56:27 -0700882 if (allowSilentUninstall && mContext.checkCallingOrSelfPermission(
883 android.Manifest.permission.DELETE_PACKAGES) == PackageManager.PERMISSION_GRANTED) {
Jeff Sharkeyf0600952014-08-07 17:31:53 -0700884 // Sweet, call straight through!
Jeff Sharkeya0907432014-08-15 10:23:11 -0700885 mPm.deletePackage(packageName, adapter.getBinder(), userId, flags);
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000886 } else if (isDeviceOwner) {
887 // Allow the DeviceOwner to silently delete packages
888 // Need to clear the calling identity to get DELETE_PACKAGES permission
889 long ident = Binder.clearCallingIdentity();
890 try {
891 mPm.deletePackage(packageName, adapter.getBinder(), userId, flags);
892 } finally {
893 Binder.restoreCallingIdentity(ident);
894 }
Jeff Sharkeyf0600952014-08-07 17:31:53 -0700895 } else {
896 // Take a short detour to confirm with user
897 final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
898 intent.setData(Uri.fromParts("package", packageName, null));
Jeff Sharkeya0907432014-08-15 10:23:11 -0700899 intent.putExtra(PackageInstaller.EXTRA_CALLBACK, adapter.getBinder().asBinder());
900 adapter.onUserActionRequired(intent);
Jeff Sharkeyf0600952014-08-07 17:31:53 -0700901 }
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700902 }
903
904 @Override
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700905 public void setPermissionsResult(int sessionId, boolean accepted) {
906 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, TAG);
907
908 synchronized (mSessions) {
Svet Ganov3baa8762016-04-08 09:22:54 -0700909 PackageInstallerSession session = mSessions.get(sessionId);
910 if (session != null) {
911 session.setPermissionsResult(accepted);
912 }
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700913 }
914 }
915
916 @Override
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700917 public void registerCallback(IPackageInstallerCallback callback, int userId) {
Amith Yamasani8cd28b52014-06-08 17:54:27 -0700918 mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "registerCallback");
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700919 mCallbacks.register(callback, userId);
Jeff Sharkeybb580672014-07-10 12:10:25 -0700920 }
921
922 @Override
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700923 public void unregisterCallback(IPackageInstallerCallback callback) {
924 mCallbacks.unregister(callback);
Jeff Sharkeya1031142014-07-12 18:09:46 -0700925 }
926
Jeff Sharkeyf174c6e2014-08-05 10:42:27 -0700927 private static int getSessionCount(SparseArray<PackageInstallerSession> sessions,
928 int installerUid) {
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700929 int count = 0;
Jeff Sharkeyf174c6e2014-08-05 10:42:27 -0700930 final int size = sessions.size();
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700931 for (int i = 0; i < size; i++) {
Jeff Sharkeyf174c6e2014-08-05 10:42:27 -0700932 final PackageInstallerSession session = sessions.valueAt(i);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700933 if (session.installerUid == installerUid) {
934 count++;
935 }
936 }
937 return count;
938 }
939
940 private boolean isCallingUidOwner(PackageInstallerSession session) {
941 final int callingUid = Binder.getCallingUid();
942 if (callingUid == Process.ROOT_UID) {
943 return true;
944 } else {
945 return (session != null) && (callingUid == session.installerUid);
Jeff Sharkeya1031142014-07-12 18:09:46 -0700946 }
947 }
948
Jeff Sharkeya0907432014-08-15 10:23:11 -0700949 static class PackageDeleteObserverAdapter extends PackageDeleteObserver {
950 private final Context mContext;
951 private final IntentSender mTarget;
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -0700952 private final String mPackageName;
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000953 private final Notification mNotification;
Jeff Sharkeya0907432014-08-15 10:23:11 -0700954
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -0700955 public PackageDeleteObserverAdapter(Context context, IntentSender target,
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000956 String packageName, boolean showNotification, int userId) {
Jeff Sharkeya0907432014-08-15 10:23:11 -0700957 mContext = context;
958 mTarget = target;
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -0700959 mPackageName = packageName;
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000960 if (showNotification) {
961 mNotification = buildSuccessNotification(mContext,
962 mContext.getResources().getString(R.string.package_deleted_device_owner),
963 packageName,
964 userId);
965 } else {
966 mNotification = null;
967 }
Jeff Sharkeya0907432014-08-15 10:23:11 -0700968 }
969
970 @Override
971 public void onUserActionRequired(Intent intent) {
972 final Intent fillIn = new Intent();
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -0700973 fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, mPackageName);
Jeff Sharkeya0907432014-08-15 10:23:11 -0700974 fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
Jeff Sharkey742e7902014-08-16 19:09:13 -0700975 PackageInstaller.STATUS_PENDING_USER_ACTION);
Jeff Sharkeya0907432014-08-15 10:23:11 -0700976 fillIn.putExtra(Intent.EXTRA_INTENT, intent);
977 try {
978 mTarget.sendIntent(mContext, 0, fillIn, null, null);
979 } catch (SendIntentException ignored) {
980 }
981 }
982
983 @Override
984 public void onPackageDeleted(String basePackageName, int returnCode, String msg) {
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000985 if (PackageManager.DELETE_SUCCEEDED == returnCode && mNotification != null) {
986 NotificationManager notificationManager = (NotificationManager)
987 mContext.getSystemService(Context.NOTIFICATION_SERVICE);
988 notificationManager.notify(basePackageName, 0, mNotification);
989 }
Jeff Sharkeya0907432014-08-15 10:23:11 -0700990 final Intent fillIn = new Intent();
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -0700991 fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, mPackageName);
Jeff Sharkeya0907432014-08-15 10:23:11 -0700992 fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
993 PackageManager.deleteStatusToPublicStatus(returnCode));
994 fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
995 PackageManager.deleteStatusToString(returnCode, msg));
996 fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode);
997 try {
998 mTarget.sendIntent(mContext, 0, fillIn, null, null);
999 } catch (SendIntentException ignored) {
1000 }
1001 }
1002 }
1003
1004 static class PackageInstallObserverAdapter extends PackageInstallObserver {
1005 private final Context mContext;
1006 private final IntentSender mTarget;
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -07001007 private final int mSessionId;
Benjamin Franz39fb7fd2015-02-18 16:11:18 +00001008 private final boolean mShowNotification;
1009 private final int mUserId;
Jeff Sharkeya0907432014-08-15 10:23:11 -07001010
Benjamin Franz39fb7fd2015-02-18 16:11:18 +00001011 public PackageInstallObserverAdapter(Context context, IntentSender target, int sessionId,
1012 boolean showNotification, int userId) {
Jeff Sharkeya0907432014-08-15 10:23:11 -07001013 mContext = context;
1014 mTarget = target;
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -07001015 mSessionId = sessionId;
Benjamin Franz39fb7fd2015-02-18 16:11:18 +00001016 mShowNotification = showNotification;
1017 mUserId = userId;
Jeff Sharkeya0907432014-08-15 10:23:11 -07001018 }
1019
1020 @Override
1021 public void onUserActionRequired(Intent intent) {
1022 final Intent fillIn = new Intent();
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -07001023 fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId);
Jeff Sharkeya0907432014-08-15 10:23:11 -07001024 fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
Jeff Sharkey742e7902014-08-16 19:09:13 -07001025 PackageInstaller.STATUS_PENDING_USER_ACTION);
Jeff Sharkeya0907432014-08-15 10:23:11 -07001026 fillIn.putExtra(Intent.EXTRA_INTENT, intent);
1027 try {
1028 mTarget.sendIntent(mContext, 0, fillIn, null, null);
1029 } catch (SendIntentException ignored) {
1030 }
1031 }
1032
1033 @Override
1034 public void onPackageInstalled(String basePackageName, int returnCode, String msg,
1035 Bundle extras) {
Benjamin Franz39fb7fd2015-02-18 16:11:18 +00001036 if (PackageManager.INSTALL_SUCCEEDED == returnCode && mShowNotification) {
Benjamin Franz2e3e9432015-04-17 15:28:17 +01001037 boolean update = (extras != null) && extras.getBoolean(Intent.EXTRA_REPLACING);
Benjamin Franz39fb7fd2015-02-18 16:11:18 +00001038 Notification notification = buildSuccessNotification(mContext,
Benjamin Franz2e3e9432015-04-17 15:28:17 +01001039 mContext.getResources()
1040 .getString(update ? R.string.package_updated_device_owner :
1041 R.string.package_installed_device_owner),
Benjamin Franz39fb7fd2015-02-18 16:11:18 +00001042 basePackageName,
1043 mUserId);
1044 if (notification != null) {
1045 NotificationManager notificationManager = (NotificationManager)
1046 mContext.getSystemService(Context.NOTIFICATION_SERVICE);
1047 notificationManager.notify(basePackageName, 0, notification);
1048 }
1049 }
Jeff Sharkeya0907432014-08-15 10:23:11 -07001050 final Intent fillIn = new Intent();
Benjamin Franz2e3e9432015-04-17 15:28:17 +01001051 fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, basePackageName);
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -07001052 fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId);
Jeff Sharkeya0907432014-08-15 10:23:11 -07001053 fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
1054 PackageManager.installStatusToPublicStatus(returnCode));
1055 fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
1056 PackageManager.installStatusToString(returnCode, msg));
1057 fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode);
1058 if (extras != null) {
1059 final String existing = extras.getString(
1060 PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE);
1061 if (!TextUtils.isEmpty(existing)) {
Jeff Sharkey941a8ba2014-08-20 16:26:32 -07001062 fillIn.putExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME, existing);
Jeff Sharkeya0907432014-08-15 10:23:11 -07001063 }
1064 }
1065 try {
1066 mTarget.sendIntent(mContext, 0, fillIn, null, null);
1067 } catch (SendIntentException ignored) {
1068 }
1069 }
1070 }
1071
Benjamin Franz39fb7fd2015-02-18 16:11:18 +00001072 /**
1073 * Build a notification for package installation / deletion by device owners that is shown if
1074 * the operation succeeds.
1075 */
1076 private static Notification buildSuccessNotification(Context context, String contentText,
1077 String basePackageName, int userId) {
1078 PackageInfo packageInfo = null;
1079 try {
1080 packageInfo = AppGlobals.getPackageManager().getPackageInfo(
1081 basePackageName, 0, userId);
1082 } catch (RemoteException ignored) {
1083 }
1084 if (packageInfo == null || packageInfo.applicationInfo == null) {
1085 Slog.w(TAG, "Notification not built for package: " + basePackageName);
1086 return null;
1087 }
1088 PackageManager pm = context.getPackageManager();
1089 Bitmap packageIcon = ImageUtils.buildScaledBitmap(
1090 packageInfo.applicationInfo.loadIcon(pm),
1091 context.getResources().getDimensionPixelSize(
1092 android.R.dimen.notification_large_icon_width),
1093 context.getResources().getDimensionPixelSize(
1094 android.R.dimen.notification_large_icon_height));
1095 CharSequence packageLabel = packageInfo.applicationInfo.loadLabel(pm);
1096 return new Notification.Builder(context)
1097 .setSmallIcon(R.drawable.ic_check_circle_24px)
1098 .setColor(context.getResources().getColor(
1099 R.color.system_notification_accent_color))
1100 .setContentTitle(packageLabel)
1101 .setContentText(contentText)
Benjamin Franz2e3e9432015-04-17 15:28:17 +01001102 .setStyle(new Notification.BigTextStyle().bigText(contentText))
Benjamin Franz39fb7fd2015-02-18 16:11:18 +00001103 .setLargeIcon(packageIcon)
1104 .build();
1105 }
1106
Jeff Sharkey54d42be2015-07-20 16:36:55 -07001107 public static <E> ArraySet<E> newArraySet(E... elements) {
1108 final ArraySet<E> set = new ArraySet<E>();
1109 if (elements != null) {
1110 set.ensureCapacity(elements.length);
1111 Collections.addAll(set, elements);
1112 }
1113 return set;
1114 }
1115
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001116 private static class Callbacks extends Handler {
1117 private static final int MSG_SESSION_CREATED = 1;
Jeff Sharkeyec9bad22014-09-05 09:45:20 -07001118 private static final int MSG_SESSION_BADGING_CHANGED = 2;
Jeff Sharkeybc7bce32014-09-05 15:53:05 -07001119 private static final int MSG_SESSION_ACTIVE_CHANGED = 3;
Jeff Sharkeyec9bad22014-09-05 09:45:20 -07001120 private static final int MSG_SESSION_PROGRESS_CHANGED = 4;
Jeff Sharkeybc7bce32014-09-05 15:53:05 -07001121 private static final int MSG_SESSION_FINISHED = 5;
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -07001122
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001123 private final RemoteCallbackList<IPackageInstallerCallback>
1124 mCallbacks = new RemoteCallbackList<>();
Jeff Sharkeya1031142014-07-12 18:09:46 -07001125
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001126 public Callbacks(Looper looper) {
1127 super(looper);
1128 }
1129
1130 public void register(IPackageInstallerCallback callback, int userId) {
1131 mCallbacks.register(callback, new UserHandle(userId));
1132 }
1133
1134 public void unregister(IPackageInstallerCallback callback) {
1135 mCallbacks.unregister(callback);
1136 }
1137
1138 @Override
1139 public void handleMessage(Message msg) {
1140 final int userId = msg.arg2;
1141 final int n = mCallbacks.beginBroadcast();
1142 for (int i = 0; i < n; i++) {
1143 final IPackageInstallerCallback callback = mCallbacks.getBroadcastItem(i);
1144 final UserHandle user = (UserHandle) mCallbacks.getBroadcastCookie(i);
1145 // TODO: dispatch notifications for slave profiles
1146 if (userId == user.getIdentifier()) {
1147 try {
1148 invokeCallback(callback, msg);
1149 } catch (RemoteException ignored) {
1150 }
Jeff Sharkeya1031142014-07-12 18:09:46 -07001151 }
1152 }
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001153 mCallbacks.finishBroadcast();
Jeff Sharkeya1031142014-07-12 18:09:46 -07001154 }
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001155
1156 private void invokeCallback(IPackageInstallerCallback callback, Message msg)
1157 throws RemoteException {
1158 final int sessionId = msg.arg1;
1159 switch (msg.what) {
1160 case MSG_SESSION_CREATED:
1161 callback.onSessionCreated(sessionId);
1162 break;
Jeff Sharkeyec9bad22014-09-05 09:45:20 -07001163 case MSG_SESSION_BADGING_CHANGED:
1164 callback.onSessionBadgingChanged(sessionId);
1165 break;
Jeff Sharkeybc7bce32014-09-05 15:53:05 -07001166 case MSG_SESSION_ACTIVE_CHANGED:
1167 callback.onSessionActiveChanged(sessionId, (boolean) msg.obj);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001168 break;
1169 case MSG_SESSION_PROGRESS_CHANGED:
1170 callback.onSessionProgressChanged(sessionId, (float) msg.obj);
1171 break;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001172 case MSG_SESSION_FINISHED:
1173 callback.onSessionFinished(sessionId, (boolean) msg.obj);
1174 break;
1175 }
1176 }
1177
1178 private void notifySessionCreated(int sessionId, int userId) {
1179 obtainMessage(MSG_SESSION_CREATED, sessionId, userId).sendToTarget();
1180 }
1181
Jeff Sharkeyec9bad22014-09-05 09:45:20 -07001182 private void notifySessionBadgingChanged(int sessionId, int userId) {
1183 obtainMessage(MSG_SESSION_BADGING_CHANGED, sessionId, userId).sendToTarget();
1184 }
1185
Jeff Sharkeybc7bce32014-09-05 15:53:05 -07001186 private void notifySessionActiveChanged(int sessionId, int userId, boolean active) {
1187 obtainMessage(MSG_SESSION_ACTIVE_CHANGED, sessionId, userId, active).sendToTarget();
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001188 }
1189
1190 private void notifySessionProgressChanged(int sessionId, int userId, float progress) {
1191 obtainMessage(MSG_SESSION_PROGRESS_CHANGED, sessionId, userId, progress).sendToTarget();
1192 }
1193
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001194 public void notifySessionFinished(int sessionId, int userId, boolean success) {
1195 obtainMessage(MSG_SESSION_FINISHED, sessionId, userId, success).sendToTarget();
1196 }
Jeff Sharkeya1031142014-07-12 18:09:46 -07001197 }
1198
1199 void dump(IndentingPrintWriter pw) {
Jeff Sharkeya1031142014-07-12 18:09:46 -07001200 synchronized (mSessions) {
Jeff Sharkey9a445772014-07-16 11:32:08 -07001201 pw.println("Active install sessions:");
1202 pw.increaseIndent();
1203 int N = mSessions.size();
Jeff Sharkeya1031142014-07-12 18:09:46 -07001204 for (int i = 0; i < N; i++) {
1205 final PackageInstallerSession session = mSessions.valueAt(i);
1206 session.dump(pw);
1207 pw.println();
1208 }
Jeff Sharkey9a445772014-07-16 11:32:08 -07001209 pw.println();
1210 pw.decreaseIndent();
1211
1212 pw.println("Historical install sessions:");
1213 pw.increaseIndent();
1214 N = mHistoricalSessions.size();
1215 for (int i = 0; i < N; i++) {
1216 final PackageInstallerSession session = mHistoricalSessions.valueAt(i);
1217 session.dump(pw);
1218 pw.println();
1219 }
1220 pw.println();
1221 pw.decreaseIndent();
Jeff Sharkey742e7902014-08-16 19:09:13 -07001222
1223 pw.println("Legacy install sessions:");
1224 pw.increaseIndent();
1225 pw.println(mLegacySessions.toString());
1226 pw.decreaseIndent();
Jeff Sharkeya1031142014-07-12 18:09:46 -07001227 }
Jeff Sharkeybb580672014-07-10 12:10:25 -07001228 }
1229
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001230 class InternalCallback {
Jeff Sharkeyec9bad22014-09-05 09:45:20 -07001231 public void onSessionBadgingChanged(PackageInstallerSession session) {
1232 mCallbacks.notifySessionBadgingChanged(session.sessionId, session.userId);
1233 writeSessionsAsync();
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001234 }
1235
Jeff Sharkeybc7bce32014-09-05 15:53:05 -07001236 public void onSessionActiveChanged(PackageInstallerSession session, boolean active) {
1237 mCallbacks.notifySessionActiveChanged(session.sessionId, session.userId, active);
Jeff Sharkey742e7902014-08-16 19:09:13 -07001238 }
1239
Jeff Sharkeyec9bad22014-09-05 09:45:20 -07001240 public void onSessionProgressChanged(PackageInstallerSession session, float progress) {
1241 mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId, progress);
1242 }
1243
Jeff Sharkeycbf47912014-09-12 09:55:32 -07001244 public void onSessionFinished(final PackageInstallerSession session, boolean success) {
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001245 mCallbacks.notifySessionFinished(session.sessionId, session.userId, success);
Jeff Sharkeycbf47912014-09-12 09:55:32 -07001246
1247 mInstallHandler.post(new Runnable() {
1248 @Override
1249 public void run() {
1250 synchronized (mSessions) {
1251 mSessions.remove(session.sessionId);
1252 mHistoricalSessions.put(session.sessionId, session);
Jeff Sharkey02bd7842014-10-06 15:14:27 -07001253
1254 final File appIconFile = buildAppIconFile(session.sessionId);
1255 if (appIconFile.exists()) {
1256 appIconFile.delete();
1257 }
1258
Jeff Sharkeycbf47912014-09-12 09:55:32 -07001259 writeSessionsLocked();
1260 }
1261 }
1262 });
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -07001263 }
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -07001264
Jeff Sharkey77d218e2014-09-06 12:20:37 -07001265 public void onSessionPrepared(PackageInstallerSession session) {
1266 // We prepared the destination to write into; we want to persist
1267 // this, but it's not critical enough to block for.
1268 writeSessionsAsync();
1269 }
1270
Jeff Sharkeycbf47912014-09-12 09:55:32 -07001271 public void onSessionSealedBlocking(PackageInstallerSession session) {
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -07001272 // It's very important that we block until we've recorded the
1273 // session as being sealed, since we never want to allow mutation
1274 // after sealing.
Jeff Sharkeycbf47912014-09-12 09:55:32 -07001275 synchronized (mSessions) {
1276 writeSessionsLocked();
1277 }
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -07001278 }
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -07001279 }
1280}