Merge "Improve performance of file copy in PackageInstaller." into udc-dev

This commit is contained in:
Alex Buynytskyy
2023-05-01 18:59:56 +00:00
committed by Android (Google) Code Review
9 changed files with 337 additions and 168 deletions

View File

@@ -2072,6 +2072,25 @@ public class PackageInstaller {
return new InstallInfo(result);
}
/**
* Parse a single APK file passed as an FD to get install relevant information about
* the package wrapped in {@link InstallInfo}.
* @throws PackageParsingException if the package source file(s) provided is(are) not valid,
* or the parser isn't able to parse the supplied source(s).
* @hide
*/
@NonNull
public InstallInfo readInstallInfo(@NonNull ParcelFileDescriptor pfd,
@Nullable String debugPathName, int flags) throws PackageParsingException {
final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
final ParseResult<PackageLite> result = ApkLiteParseUtils.parseMonolithicPackageLite(input,
pfd.getFileDescriptor(), debugPathName, flags);
if (result.isError()) {
throw new PackageParsingException(result.getErrorCode(), result.getErrorMessage());
}
return new InstallInfo(result);
}
// (b/239722738) This class serves as a bridge between the PackageLite class, which
// is a hidden class, and the consumers of this class. (e.g. InstallInstalling.java)
// This is a part of an effort to remove dependency on hidden APIs and use SystemAPIs or
@@ -2125,6 +2144,21 @@ public class PackageInstaller {
public long calculateInstalledSize(@NonNull SessionParams params) throws IOException {
return InstallLocationUtils.calculateInstalledSize(mPkg, params.abiOverride);
}
/**
* @param params {@link SessionParams} of the installation
* @param pfd of an APK opened for read
* @return Total disk space occupied by an application after installation.
* Includes the size of the raw APKs, possibly unpacked resources, raw dex metadata files,
* and all relevant native code.
* @throws IOException when size of native binaries cannot be calculated.
* @hide
*/
public long calculateInstalledSize(@NonNull SessionParams params,
@NonNull ParcelFileDescriptor pfd) throws IOException {
return InstallLocationUtils.calculateInstalledSize(mPkg, params.abiOverride,
pfd.getFileDescriptor());
}
}
/**

View File

@@ -109,7 +109,7 @@ public class ApkLiteParseUtils {
}
/**
* Parse lightweight details about a single APK files.
* Parse lightweight details about a single APK file.
*/
public static ParseResult<PackageLite> parseMonolithicPackageLite(ParseInput input,
File packageFile, int flags) {
@@ -134,6 +134,33 @@ public class ApkLiteParseUtils {
}
}
/**
* Parse lightweight details about a single APK file passed as an FD.
*/
public static ParseResult<PackageLite> parseMonolithicPackageLite(ParseInput input,
FileDescriptor packageFd, String debugPathName, int flags) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite");
try {
final ParseResult<ApkLite> result = parseApkLite(input, packageFd, debugPathName,
flags);
if (result.isError()) {
return input.error(result);
}
final ApkLite baseApk = result.getResult();
final String packagePath = debugPathName;
return input.success(
new PackageLite(packagePath, baseApk.getPath(), baseApk, null /* splitNames */,
null /* isFeatureSplits */, null /* usesSplitNames */,
null /* configForSplit */, null /* splitApkPaths */,
null /* splitRevisionCodes */, baseApk.getTargetSdkVersion(),
null /* requiredSplitTypes */, null, /* splitTypes */
baseApk.isAllowUpdateOwnership()));
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
/**
* Parse lightweight details about a directory of APKs.
*

View File

@@ -16,14 +16,14 @@
package com.android.packageinstaller;
import static com.android.packageinstaller.PackageInstallerActivity.EXTRA_STAGED_SESSION_ID;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import androidx.annotation.Nullable;
import java.io.File;
/**
* Trampoline activity. Calls PackageInstallerActivity and deletes staged install file onResult.
*/
@@ -52,8 +52,13 @@ public class DeleteStagedFileOnResult extends Activity {
super.onDestroy();
if (isFinishing()) {
File sourceFile = new File(getIntent().getData().getPath());
new Thread(sourceFile::delete).start();
// While we expect PIA/InstallStaging to abandon/commit the session, still there
// might be cases when the session becomes orphan.
int sessionId = getIntent().getIntExtra(EXTRA_STAGED_SESSION_ID, 0);
try {
getPackageManager().getPackageInstaller().abandonSession(sessionId);
} catch (SecurityException ignored) {
}
}
}
}

View File

@@ -16,17 +16,17 @@
package com.android.packageinstaller;
import static com.android.packageinstaller.PackageInstallerActivity.EXTRA_STAGED_SESSION_ID;
import android.app.PendingIntent;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.InstallInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Process;
import android.util.Log;
import android.view.View;
import android.widget.Button;
@@ -34,10 +34,7 @@ import android.widget.Button;
import androidx.annotation.Nullable;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Send package to the package manager and handle results from package manager. Once the
@@ -77,7 +74,7 @@ public class InstallInstalling extends AlertActivity {
.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
mPackageURI = getIntent().getData();
if ("package".equals(mPackageURI.getScheme())) {
if (PackageInstallerActivity.SCHEME_PACKAGE.equals(mPackageURI.getScheme())) {
try {
getPackageManager().installExistingPackage(appInfo.packageName);
launchSuccess();
@@ -86,6 +83,8 @@ public class InstallInstalling extends AlertActivity {
PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
}
} else {
// ContentResolver.SCHEME_FILE
// STAGED_SESSION_ID extra contains an ID of a previously staged install session.
final File sourceFile = new File(mPackageURI.getPath());
PackageUtil.AppSnippet as = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
@@ -122,41 +121,6 @@ public class InstallInstalling extends AlertActivity {
// Does not happen
}
} else {
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
final Uri referrerUri = getIntent().getParcelableExtra(Intent.EXTRA_REFERRER);
params.setPackageSource(
referrerUri != null ? PackageInstaller.PACKAGE_SOURCE_DOWNLOADED_FILE
: PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE);
params.setInstallAsInstantApp(false);
params.setReferrerUri(referrerUri);
params.setOriginatingUri(getIntent()
.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI));
params.setOriginatingUid(getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID,
Process.INVALID_UID));
params.setInstallerPackageName(getIntent().getStringExtra(
Intent.EXTRA_INSTALLER_PACKAGE_NAME));
params.setInstallReason(PackageManager.INSTALL_REASON_USER);
File file = new File(mPackageURI.getPath());
try {
final InstallInfo result = getPackageManager().getPackageInstaller()
.readInstallInfo(file, 0);
params.setAppPackageName(result.getPackageName());
params.setInstallLocation(result.getInstallLocation());
try {
params.setSize(result.calculateInstalledSize(params));
} catch (IOException e) {
e.printStackTrace();
params.setSize(file.length());
}
} catch (PackageInstaller.PackageParsingException e) {
Log.e(LOG_TAG, "Cannot parse package " + file + ". Assuming defaults.", e);
Log.e(LOG_TAG,
"Cannot calculate installed size " + file + ". Try only apk size.");
params.setSize(file.length());
}
try {
mInstallId = InstallEventReceiver
.addObserver(this, EventResultPersister.GENERATE_NEW_ID,
@@ -166,9 +130,14 @@ public class InstallInstalling extends AlertActivity {
PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
}
try {
mSessionId = getPackageManager().getPackageInstaller().createSession(params);
} catch (IOException e) {
mSessionId = getIntent().getIntExtra(EXTRA_STAGED_SESSION_ID, 0);
// Try to open session previously staged in InstallStaging.
try (PackageInstaller.Session ignored =
getPackageManager().getPackageInstaller().openSession(
mSessionId)) {
Log.d(LOG_TAG, "Staged session is valid, proceeding with the install");
} catch (IOException | SecurityException e) {
Log.e(LOG_TAG, "Invalid session id passed", e);
launchFailure(PackageInstaller.STATUS_FAILURE,
PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
}
@@ -293,57 +262,9 @@ public class InstallInstalling extends AlertActivity {
@Override
protected PackageInstaller.Session doInBackground(Void... params) {
PackageInstaller.Session session;
try {
session = getPackageManager().getPackageInstaller().openSession(mSessionId);
return getPackageManager().getPackageInstaller().openSession(mSessionId);
} catch (IOException e) {
synchronized (this) {
isDone = true;
notifyAll();
}
return null;
}
session.setStagingProgress(0);
try {
File file = new File(mPackageURI.getPath());
try (InputStream in = new FileInputStream(file)) {
long sizeBytes = file.length();
long totalRead = 0;
try (OutputStream out = session
.openWrite("PackageInstaller", 0, sizeBytes)) {
byte[] buffer = new byte[1024 * 1024];
while (true) {
int numRead = in.read(buffer);
if (numRead == -1) {
session.fsync(out);
break;
}
if (isCancelled()) {
session.close();
break;
}
out.write(buffer, 0, numRead);
if (sizeBytes > 0) {
totalRead += numRead;
float fraction = ((float) totalRead / (float) sizeBytes);
session.setStagingProgress(fraction);
}
}
}
}
return session;
} catch (IOException | SecurityException e) {
Log.e(LOG_TAG, "Could not write package", e);
session.close();
return null;
} finally {
synchronized (this) {

View File

@@ -16,6 +16,10 @@
package com.android.packageinstaller;
import static android.content.res.AssetFileDescriptor.UNKNOWN_LENGTH;
import static com.android.packageinstaller.PackageInstallerActivity.EXTRA_STAGED_SESSION_ID;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
@@ -23,40 +27,49 @@ import android.app.DialogFragment;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.res.AssetFileDescriptor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.util.Log;
import android.view.View;
import android.widget.ProgressBar;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* If a package gets installed from an content URI this step loads the package and turns it into
* and installation from a file. Then it re-starts the installation as usual.
* If a package gets installed from a content URI this step stages the installation session
* reading bytes from the URI.
*/
public class InstallStaging extends AlertActivity {
private static final String LOG_TAG = InstallStaging.class.getSimpleName();
private static final String STAGED_FILE = "STAGED_FILE";
private static final String STAGED_SESSION_ID = "STAGED_SESSION_ID";
private @Nullable PackageInstaller mInstaller;
/** Currently running task that loads the file from the content URI into a file */
private @Nullable StagingAsyncTask mStagingTask;
/** The file the package is in */
private @Nullable File mStagedFile;
/** The session the package is in */
private int mStagedSessionId;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mInstaller = getPackageManager().getPackageInstaller();
setFinishOnTouchOutside(true);
mAlert.setIcon(R.drawable.ic_file_download);
mAlert.setTitle(getString(R.string.app_name_unknown));
@@ -66,6 +79,9 @@ public class InstallStaging extends AlertActivity {
if (mStagingTask != null) {
mStagingTask.cancel(true);
}
cleanupStagingSession();
setResult(RESULT_CANCELED);
finish();
}, null);
@@ -73,11 +89,7 @@ public class InstallStaging extends AlertActivity {
requireViewById(R.id.staging).setVisibility(View.VISIBLE);
if (savedInstanceState != null) {
mStagedFile = new File(savedInstanceState.getString(STAGED_FILE));
if (!mStagedFile.exists()) {
mStagedFile = null;
}
mStagedSessionId = savedInstanceState.getInt(STAGED_SESSION_ID, 0);
}
}
@@ -85,21 +97,41 @@ public class InstallStaging extends AlertActivity {
protected void onResume() {
super.onResume();
// This is the first onResume in a single life of the activity
// This is the first onResume in a single life of the activity.
if (mStagingTask == null) {
// File does not exist, or became invalid
if (mStagedFile == null) {
// Create file delayed to be able to show error
if (mStagedSessionId > 0) {
final PackageInstaller.SessionInfo info = mInstaller.getSessionInfo(
mStagedSessionId);
if (info == null || !info.isActive() || info.getResolvedBaseApkPath() == null) {
Log.w(LOG_TAG, "Session " + mStagedSessionId + " in funky state; ignoring");
if (info != null) {
cleanupStagingSession();
}
mStagedSessionId = 0;
}
}
// Session does not exist, or became invalid.
if (mStagedSessionId <= 0) {
// Create session here to be able to show error.
final Uri packageUri = getIntent().getData();
final AssetFileDescriptor afd = openAssetFileDescriptor(packageUri);
try {
mStagedFile = TemporaryFileManager.getStagedFile(this);
ParcelFileDescriptor pfd = afd != null ? afd.getParcelFileDescriptor() : null;
PackageInstaller.SessionParams params = createSessionParams(
mInstaller, getIntent(), pfd, packageUri.toString());
mStagedSessionId = mInstaller.createSession(params);
} catch (IOException e) {
Log.w(LOG_TAG, "Failed to create a staging session", e);
showError();
return;
} finally {
PackageUtil.safeClose(afd);
}
}
mStagingTask = new StagingAsyncTask();
mStagingTask.execute(getIntent().getData());
mStagingTask.execute();
}
}
@@ -107,7 +139,7 @@ public class InstallStaging extends AlertActivity {
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(STAGED_FILE, mStagedFile.getPath());
outState.putInt(STAGED_SESSION_ID, mStagedSessionId);
}
@Override
@@ -119,6 +151,65 @@ public class InstallStaging extends AlertActivity {
super.onDestroy();
}
private AssetFileDescriptor openAssetFileDescriptor(Uri uri) {
try {
return getContentResolver().openAssetFileDescriptor(uri, "r");
} catch (Exception e) {
Log.w(LOG_TAG, "Failed to open asset file descriptor", e);
return null;
}
}
private static PackageInstaller.SessionParams createSessionParams(
@NonNull PackageInstaller installer, @NonNull Intent intent,
@Nullable ParcelFileDescriptor pfd, @NonNull String debugPathName) {
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
final Uri referrerUri = intent.getParcelableExtra(Intent.EXTRA_REFERRER);
params.setPackageSource(
referrerUri != null ? PackageInstaller.PACKAGE_SOURCE_DOWNLOADED_FILE
: PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE);
params.setInstallAsInstantApp(false);
params.setReferrerUri(referrerUri);
params.setOriginatingUri(intent
.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI));
params.setOriginatingUid(intent.getIntExtra(Intent.EXTRA_ORIGINATING_UID,
Process.INVALID_UID));
params.setInstallerPackageName(intent.getStringExtra(
Intent.EXTRA_INSTALLER_PACKAGE_NAME));
params.setInstallReason(PackageManager.INSTALL_REASON_USER);
if (pfd != null) {
try {
final PackageInstaller.InstallInfo result = installer.readInstallInfo(pfd,
debugPathName, 0);
params.setAppPackageName(result.getPackageName());
params.setInstallLocation(result.getInstallLocation());
params.setSize(result.calculateInstalledSize(params, pfd));
} catch (PackageInstaller.PackageParsingException | IOException e) {
Log.e(LOG_TAG, "Cannot parse package " + debugPathName + ". Assuming defaults.", e);
Log.e(LOG_TAG,
"Cannot calculate installed size " + debugPathName
+ ". Try only apk size.");
params.setSize(pfd.getStatSize());
}
} else {
Log.e(LOG_TAG, "Cannot parse package " + debugPathName + ". Assuming defaults.");
}
return params;
}
private void cleanupStagingSession() {
if (mStagedSessionId > 0) {
try {
mInstaller.abandonSession(mStagedSessionId);
} catch (SecurityException ignored) {
}
mStagedSessionId = 0;
}
}
/**
* Show an error message and set result as error.
*/
@@ -165,58 +256,109 @@ public class InstallStaging extends AlertActivity {
}
}
private final class StagingAsyncTask extends AsyncTask<Uri, Void, Boolean> {
@Override
protected Boolean doInBackground(Uri... params) {
if (params == null || params.length <= 0) {
return false;
}
Uri packageUri = params[0];
try (InputStream in = getContentResolver().openInputStream(packageUri)) {
// Despite the comments in ContentResolver#openInputStream the returned stream can
// be null.
if (in == null) {
return false;
}
private final class StagingAsyncTask extends
AsyncTask<Void, Integer, PackageInstaller.SessionInfo> {
private ProgressBar mProgressBar = null;
try (OutputStream out = new FileOutputStream(mStagedFile)) {
byte[] buffer = new byte[1024 * 1024];
int bytesRead;
while ((bytesRead = in.read(buffer)) >= 0) {
// Be nice and respond to a cancellation
if (isCancelled()) {
return false;
}
out.write(buffer, 0, bytesRead);
}
}
} catch (IOException | SecurityException | IllegalStateException
| IllegalArgumentException e) {
Log.w(LOG_TAG, "Error staging apk from content URI", e);
return false;
private long getContentSizeBytes() {
try (AssetFileDescriptor afd = openAssetFileDescriptor(getIntent().getData())) {
return afd != null ? afd.getLength() : UNKNOWN_LENGTH;
} catch (IOException ignored) {
return UNKNOWN_LENGTH;
}
return true;
}
@Override
protected void onPostExecute(Boolean success) {
if (success) {
// Now start the installation again from a file
Intent installIntent = new Intent(getIntent());
installIntent.setClass(InstallStaging.this, DeleteStagedFileOnResult.class);
installIntent.setData(Uri.fromFile(mStagedFile));
protected void onPreExecute() {
final long sizeBytes = getContentSizeBytes();
if (installIntent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
installIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
mProgressBar = sizeBytes > 0 ? requireViewById(R.id.progress_indeterminate) : null;
if (mProgressBar != null) {
mProgressBar.setProgress(0);
mProgressBar.setMax(100);
mProgressBar.setIndeterminate(false);
}
}
@Override
protected PackageInstaller.SessionInfo doInBackground(Void... params) {
Uri packageUri = getIntent().getData();
try (PackageInstaller.Session session = mInstaller.openSession(mStagedSessionId);
InputStream in = getContentResolver().openInputStream(packageUri)) {
session.setStagingProgress(0);
if (in == null) {
return null;
}
installIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
startActivity(installIntent);
long sizeBytes = getContentSizeBytes();
InstallStaging.this.finish();
} else {
showError();
long totalRead = 0;
try (OutputStream out = session.openWrite("PackageInstaller", 0, sizeBytes)) {
byte[] buffer = new byte[1024 * 1024];
while (true) {
int numRead = in.read(buffer);
if (numRead == -1) {
session.fsync(out);
break;
}
if (isCancelled()) {
break;
}
out.write(buffer, 0, numRead);
if (sizeBytes > 0) {
totalRead += numRead;
float fraction = ((float) totalRead / (float) sizeBytes);
session.setStagingProgress(fraction);
publishProgress((int) (fraction * 100.0));
}
}
}
return mInstaller.getSessionInfo(mStagedSessionId);
} catch (IOException | SecurityException | IllegalStateException
| IllegalArgumentException e) {
Log.w(LOG_TAG, "Error staging apk from content URI", e);
return null;
}
}
@Override
protected void onProgressUpdate(Integer... progress) {
if (mProgressBar != null && progress != null && progress.length > 0) {
mProgressBar.setProgress(progress[0], true);
}
}
@Override
protected void onPostExecute(PackageInstaller.SessionInfo sessionInfo) {
if (sessionInfo == null || !sessionInfo.isActive()
|| sessionInfo.getResolvedBaseApkPath() == null) {
Log.w(LOG_TAG, "Session info is invalid: " + sessionInfo);
cleanupStagingSession();
showError();
return;
}
// Pass the staged session to the installer.
Intent installIntent = new Intent(getIntent());
installIntent.setClass(InstallStaging.this, DeleteStagedFileOnResult.class);
installIntent.setData(Uri.fromFile(new File(sessionInfo.getResolvedBaseApkPath())));
installIntent.putExtra(EXTRA_STAGED_SESSION_ID, mStagedSessionId);
if (installIntent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
installIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
}
installIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
startActivity(installIntent);
InstallStaging.this.finish();
}
}
}

View File

@@ -148,10 +148,11 @@ public class InstallStart extends Activity {
// [IMPORTANT] This path is deprecated, but should still work. Only necessary
// features should be added.
// Copy file to prevent it from being changed underneath this process
// Stage a session with this file to prevent it from being changed underneath
// this process.
nextActivity.setClass(this, InstallStaging.class);
} else if (packageUri != null && packageUri.getScheme().equals(
PackageInstallerActivity.SCHEME_PACKAGE)) {
} else if (packageUri != null && PackageInstallerActivity.SCHEME_PACKAGE.equals(
packageUri.getScheme())) {
nextActivity.setClass(this, PackageInstallerActivity.class);
} else {
Intent result = new Intent();

View File

@@ -82,6 +82,7 @@ public class PackageInstallerActivity extends AlertActivity {
static final String EXTRA_CALLING_PACKAGE = "EXTRA_CALLING_PACKAGE";
static final String EXTRA_CALLING_ATTRIBUTION_TAG = "EXTRA_CALLING_ATTRIBUTION_TAG";
static final String EXTRA_ORIGINAL_SOURCE_INFO = "EXTRA_ORIGINAL_SOURCE_INFO";
static final String EXTRA_STAGED_SESSION_ID = "EXTRA_STAGED_SESSION_ID";
private static final String ALLOW_UNKNOWN_SOURCES_KEY =
PackageInstallerActivity.class.getName() + "ALLOW_UNKNOWN_SOURCES_KEY";
@@ -403,6 +404,10 @@ public class PackageInstallerActivity extends AlertActivity {
mReferrerURI = null;
mPendingUserActionReason = info.getPendingUserActionReason();
} else {
// Two possible callers:
// 1. InstallStart with "SCHEME_PACKAGE".
// 2. InstallStaging with "SCHEME_FILE" and EXTRA_STAGED_SESSION_ID with staged
// session id.
mSessionId = -1;
packageSource = intent.getData();
mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
@@ -721,14 +726,16 @@ public class PackageInstallerActivity extends AlertActivity {
}
private void startInstall() {
String installerPackageName = getIntent().getStringExtra(
Intent.EXTRA_INSTALLER_PACKAGE_NAME);
int stagedSessionId = getIntent().getIntExtra(EXTRA_STAGED_SESSION_ID, 0);
// Start subactivity to actually install the application
Intent newIntent = new Intent();
newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
mPkgInfo.applicationInfo);
newIntent.setData(mPackageURI);
newIntent.setClass(this, InstallInstalling.class);
String installerPackageName = getIntent().getStringExtra(
Intent.EXTRA_INSTALLER_PACKAGE_NAME);
if (mOriginatingURI != null) {
newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI);
}
@@ -745,6 +752,9 @@ public class PackageInstallerActivity extends AlertActivity {
if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
}
if (stagedSessionId > 0) {
newIntent.putExtra(EXTRA_STAGED_SESSION_ID, stagedSessionId);
}
newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
if (mLocalLOGV) Log.i(TAG, "downloaded app uri=" + mPackageURI);
startActivity(newIntent);

View File

@@ -33,7 +33,9 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
/**
* This is a utility class for defining some utility methods and constants
@@ -190,4 +192,19 @@ public class PackageUtil {
}
return targetSdkVersion;
}
/**
* Quietly close a closeable resource (e.g. a stream or file). The input may already
* be closed and it may even be null.
*/
static void safeClose(Closeable resource) {
if (resource != null) {
try {
resource.close();
} catch (IOException ioe) {
// Catch and discard the error
}
}
}
}

View File

@@ -1148,12 +1148,21 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
info.userId = userId;
info.installerPackageName = mInstallSource.mInstallerPackageName;
info.installerAttributionTag = mInstallSource.mInstallerAttributionTag;
info.resolvedBaseCodePath = null;
if (mContext.checkCallingOrSelfPermission(
Manifest.permission.READ_INSTALLED_SESSION_PATHS)
== PackageManager.PERMISSION_GRANTED && mResolvedBaseFile != null) {
info.resolvedBaseCodePath = mResolvedBaseFile.getAbsolutePath();
} else {
info.resolvedBaseCodePath = null;
== PackageManager.PERMISSION_GRANTED) {
File file = mResolvedBaseFile;
if (file == null) {
// Try to guess mResolvedBaseFile file.
final List<File> addedFiles = getAddedApksLocked();
if (addedFiles.size() > 0) {
file = addedFiles.get(0);
}
}
if (file != null) {
info.resolvedBaseCodePath = file.getAbsolutePath();
}
}
info.progress = progress;
info.sealed = mSealed;
@@ -1355,9 +1364,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@GuardedBy("mLock")
private String[] getStageDirContentsLocked() {
if (stageDir == null) {
return EmptyArray.STRING;
}
String[] result = stageDir.list();
if (result == null) {
result = EmptyArray.STRING;
return EmptyArray.STRING;
}
return result;
}