* commit '1d5ef81e29279f769d79315f2a38e747ea92e10f': Reduce PackageInstaller I/O pressure.
This commit is contained in:
@@ -880,6 +880,8 @@ public class PackageInstaller {
|
|||||||
/** {@hide} */
|
/** {@hide} */
|
||||||
public String appLabel;
|
public String appLabel;
|
||||||
/** {@hide} */
|
/** {@hide} */
|
||||||
|
public long appIconLastModified = -1;
|
||||||
|
/** {@hide} */
|
||||||
public Uri originatingUri;
|
public Uri originatingUri;
|
||||||
/** {@hide} */
|
/** {@hide} */
|
||||||
public Uri referrerUri;
|
public Uri referrerUri;
|
||||||
|
|||||||
@@ -1503,6 +1503,7 @@ public class XmlUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public static void writeBitmapAttribute(XmlSerializer out, String name, Bitmap value)
|
public static void writeBitmapAttribute(XmlSerializer out, String name, Bitmap value)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import static com.android.internal.util.XmlUtils.readIntAttribute;
|
|||||||
import static com.android.internal.util.XmlUtils.readLongAttribute;
|
import static com.android.internal.util.XmlUtils.readLongAttribute;
|
||||||
import static com.android.internal.util.XmlUtils.readStringAttribute;
|
import static com.android.internal.util.XmlUtils.readStringAttribute;
|
||||||
import static com.android.internal.util.XmlUtils.readUriAttribute;
|
import static com.android.internal.util.XmlUtils.readUriAttribute;
|
||||||
import static com.android.internal.util.XmlUtils.writeBitmapAttribute;
|
|
||||||
import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
|
import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
|
||||||
import static com.android.internal.util.XmlUtils.writeIntAttribute;
|
import static com.android.internal.util.XmlUtils.writeIntAttribute;
|
||||||
import static com.android.internal.util.XmlUtils.writeLongAttribute;
|
import static com.android.internal.util.XmlUtils.writeLongAttribute;
|
||||||
@@ -47,6 +46,8 @@ import android.content.pm.PackageInstaller.SessionInfo;
|
|||||||
import android.content.pm.PackageInstaller.SessionParams;
|
import android.content.pm.PackageInstaller.SessionParams;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Bitmap.CompressFormat;
|
||||||
|
import android.graphics.BitmapFactory;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@@ -69,7 +70,6 @@ import android.text.format.DateUtils;
|
|||||||
import android.util.ArraySet;
|
import android.util.ArraySet;
|
||||||
import android.util.AtomicFile;
|
import android.util.AtomicFile;
|
||||||
import android.util.ExceptionUtils;
|
import android.util.ExceptionUtils;
|
||||||
import android.util.Log;
|
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
import android.util.SparseBooleanArray;
|
import android.util.SparseBooleanArray;
|
||||||
@@ -125,6 +125,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
|
|||||||
private static final String ATTR_INSTALL_LOCATION = "installLocation";
|
private static final String ATTR_INSTALL_LOCATION = "installLocation";
|
||||||
private static final String ATTR_SIZE_BYTES = "sizeBytes";
|
private static final String ATTR_SIZE_BYTES = "sizeBytes";
|
||||||
private static final String ATTR_APP_PACKAGE_NAME = "appPackageName";
|
private static final String ATTR_APP_PACKAGE_NAME = "appPackageName";
|
||||||
|
@Deprecated
|
||||||
private static final String ATTR_APP_ICON = "appIcon";
|
private static final String ATTR_APP_ICON = "appIcon";
|
||||||
private static final String ATTR_APP_LABEL = "appLabel";
|
private static final String ATTR_APP_LABEL = "appLabel";
|
||||||
private static final String ATTR_ORIGINATING_URI = "originatingUri";
|
private static final String ATTR_ORIGINATING_URI = "originatingUri";
|
||||||
@@ -149,10 +150,16 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
|
|||||||
private final Callbacks mCallbacks;
|
private final Callbacks mCallbacks;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* File storing persisted {@link #mSessions}.
|
* File storing persisted {@link #mSessions} metadata.
|
||||||
*/
|
*/
|
||||||
private final AtomicFile mSessionsFile;
|
private final AtomicFile mSessionsFile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Directory storing persisted {@link #mSessions} metadata which is too
|
||||||
|
* heavy to store directly in {@link #mSessionsFile}.
|
||||||
|
*/
|
||||||
|
private final File mSessionsDir;
|
||||||
|
|
||||||
private final InternalCallback mInternalCallback = new InternalCallback();
|
private final InternalCallback mInternalCallback = new InternalCallback();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -195,26 +202,38 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
|
|||||||
|
|
||||||
mSessionsFile = new AtomicFile(
|
mSessionsFile = new AtomicFile(
|
||||||
new File(Environment.getSystemSecureDirectory(), "install_sessions.xml"));
|
new File(Environment.getSystemSecureDirectory(), "install_sessions.xml"));
|
||||||
|
mSessionsDir = new File(Environment.getSystemSecureDirectory(), "install_sessions");
|
||||||
|
mSessionsDir.mkdirs();
|
||||||
|
|
||||||
synchronized (mSessions) {
|
synchronized (mSessions) {
|
||||||
readSessionsLocked();
|
readSessionsLocked();
|
||||||
|
|
||||||
final ArraySet<File> unclaimed = Sets.newArraySet(mStagingDir.listFiles(sStageFilter));
|
final ArraySet<File> unclaimedStages = Sets.newArraySet(
|
||||||
|
mStagingDir.listFiles(sStageFilter));
|
||||||
|
final ArraySet<File> unclaimedIcons = Sets.newArraySet(
|
||||||
|
mSessionsDir.listFiles());
|
||||||
|
|
||||||
// Ignore stages claimed by active sessions
|
// Ignore stages and icons claimed by active sessions
|
||||||
for (int i = 0; i < mSessions.size(); i++) {
|
for (int i = 0; i < mSessions.size(); i++) {
|
||||||
final PackageInstallerSession session = mSessions.valueAt(i);
|
final PackageInstallerSession session = mSessions.valueAt(i);
|
||||||
unclaimed.remove(session.stageDir);
|
unclaimedStages.remove(session.stageDir);
|
||||||
|
unclaimedIcons.remove(buildAppIconFile(session.sessionId));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up orphaned staging directories
|
// Clean up orphaned staging directories
|
||||||
for (File stage : unclaimed) {
|
for (File stage : unclaimedStages) {
|
||||||
Slog.w(TAG, "Deleting orphan stage " + stage);
|
Slog.w(TAG, "Deleting orphan stage " + stage);
|
||||||
if (stage.isDirectory()) {
|
if (stage.isDirectory()) {
|
||||||
FileUtils.deleteContents(stage);
|
FileUtils.deleteContents(stage);
|
||||||
}
|
}
|
||||||
stage.delete();
|
stage.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clean up orphaned icons
|
||||||
|
for (File icon : unclaimedIcons) {
|
||||||
|
Slog.w(TAG, "Deleting orphan icon " + icon);
|
||||||
|
icon.delete();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,6 +378,12 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
|
|||||||
params.referrerUri = readUriAttribute(in, ATTR_REFERRER_URI);
|
params.referrerUri = readUriAttribute(in, ATTR_REFERRER_URI);
|
||||||
params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE);
|
params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE);
|
||||||
|
|
||||||
|
final File appIconFile = buildAppIconFile(sessionId);
|
||||||
|
if (appIconFile.exists()) {
|
||||||
|
params.appIcon = BitmapFactory.decodeFile(appIconFile.getAbsolutePath());
|
||||||
|
params.appIconLastModified = appIconFile.lastModified();
|
||||||
|
}
|
||||||
|
|
||||||
return new PackageInstallerSession(mInternalCallback, mContext, mPm,
|
return new PackageInstallerSession(mInternalCallback, mContext, mPm,
|
||||||
mInstallThread.getLooper(), sessionId, userId, installerPackageName, installerUid,
|
mInstallThread.getLooper(), sessionId, userId, installerPackageName, installerUid,
|
||||||
params, createdMillis, stageDir, stageCid, prepared, sealed);
|
params, createdMillis, stageDir, stageCid, prepared, sealed);
|
||||||
@@ -418,15 +443,38 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
|
|||||||
writeIntAttribute(out, ATTR_INSTALL_LOCATION, params.installLocation);
|
writeIntAttribute(out, ATTR_INSTALL_LOCATION, params.installLocation);
|
||||||
writeLongAttribute(out, ATTR_SIZE_BYTES, params.sizeBytes);
|
writeLongAttribute(out, ATTR_SIZE_BYTES, params.sizeBytes);
|
||||||
writeStringAttribute(out, ATTR_APP_PACKAGE_NAME, params.appPackageName);
|
writeStringAttribute(out, ATTR_APP_PACKAGE_NAME, params.appPackageName);
|
||||||
writeBitmapAttribute(out, ATTR_APP_ICON, params.appIcon);
|
|
||||||
writeStringAttribute(out, ATTR_APP_LABEL, params.appLabel);
|
writeStringAttribute(out, ATTR_APP_LABEL, params.appLabel);
|
||||||
writeUriAttribute(out, ATTR_ORIGINATING_URI, params.originatingUri);
|
writeUriAttribute(out, ATTR_ORIGINATING_URI, params.originatingUri);
|
||||||
writeUriAttribute(out, ATTR_REFERRER_URI, params.referrerUri);
|
writeUriAttribute(out, ATTR_REFERRER_URI, params.referrerUri);
|
||||||
writeStringAttribute(out, ATTR_ABI_OVERRIDE, params.abiOverride);
|
writeStringAttribute(out, ATTR_ABI_OVERRIDE, params.abiOverride);
|
||||||
|
|
||||||
|
// Persist app icon if changed since last written
|
||||||
|
final File appIconFile = buildAppIconFile(session.sessionId);
|
||||||
|
if (params.appIcon == null && appIconFile.exists()) {
|
||||||
|
appIconFile.delete();
|
||||||
|
} else if (params.appIcon != null
|
||||||
|
&& appIconFile.lastModified() != params.appIconLastModified) {
|
||||||
|
if (LOGD) Slog.w(TAG, "Writing changed icon " + appIconFile);
|
||||||
|
FileOutputStream os = null;
|
||||||
|
try {
|
||||||
|
os = new FileOutputStream(appIconFile);
|
||||||
|
params.appIcon.compress(CompressFormat.PNG, 90, os);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Slog.w(TAG, "Failed to write icon " + appIconFile + ": " + e.getMessage());
|
||||||
|
} finally {
|
||||||
|
IoUtils.closeQuietly(os);
|
||||||
|
}
|
||||||
|
|
||||||
|
params.appIconLastModified = appIconFile.lastModified();
|
||||||
|
}
|
||||||
|
|
||||||
out.endTag(null, TAG_SESSION);
|
out.endTag(null, TAG_SESSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private File buildAppIconFile(int sessionId) {
|
||||||
|
return new File(mSessionsDir, "app_icon." + sessionId + ".png");
|
||||||
|
}
|
||||||
|
|
||||||
private void writeSessionsAsync() {
|
private void writeSessionsAsync() {
|
||||||
IoThread.getHandler().post(new Runnable() {
|
IoThread.getHandler().post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
@@ -548,7 +596,21 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
|
|||||||
if (session == null || !isCallingUidOwner(session)) {
|
if (session == null || !isCallingUidOwner(session)) {
|
||||||
throw new SecurityException("Caller has no access to session " + sessionId);
|
throw new SecurityException("Caller has no access to session " + sessionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Defensively resize giant app icons
|
||||||
|
if (appIcon != null) {
|
||||||
|
final ActivityManager am = (ActivityManager) mContext.getSystemService(
|
||||||
|
Context.ACTIVITY_SERVICE);
|
||||||
|
final int iconSize = am.getLauncherLargeIconSize();
|
||||||
|
if ((appIcon.getWidth() > iconSize * 2)
|
||||||
|
|| (appIcon.getHeight() > iconSize * 2)) {
|
||||||
|
appIcon = Bitmap.createScaledBitmap(appIcon, iconSize, iconSize, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
session.params.appIcon = appIcon;
|
session.params.appIcon = appIcon;
|
||||||
|
session.params.appIconLastModified = -1;
|
||||||
|
|
||||||
mInternalCallback.onSessionBadgingChanged(session);
|
mInternalCallback.onSessionBadgingChanged(session);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -973,6 +1035,12 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
|
|||||||
synchronized (mSessions) {
|
synchronized (mSessions) {
|
||||||
mSessions.remove(session.sessionId);
|
mSessions.remove(session.sessionId);
|
||||||
mHistoricalSessions.put(session.sessionId, session);
|
mHistoricalSessions.put(session.sessionId, session);
|
||||||
|
|
||||||
|
final File appIconFile = buildAppIconFile(session.sessionId);
|
||||||
|
if (appIconFile.exists()) {
|
||||||
|
appIconFile.delete();
|
||||||
|
}
|
||||||
|
|
||||||
writeSessionsLocked();
|
writeSessionsLocked();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user