Use modern install method in backup service

Test: adb backup -apk myApp -f backup ; adb restore backup
Change-Id: I2bbcb0487ecc525a6dc8a2693b15ce624db275b0
This commit is contained in:
Philip P. Moltmann
2017-12-13 15:59:07 -08:00
parent ec397abf93
commit 79c238ada6
14 changed files with 134 additions and 290 deletions

View File

@@ -64,7 +64,6 @@ import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -1682,22 +1681,6 @@ public class ApplicationPackageManager extends PackageManager {
return info.loadLabel(this);
}
@Override
public void installPackage(Uri packageURI,
PackageInstallObserver observer, int flags, String installerPackageName) {
if (!"file".equals(packageURI.getScheme())) {
throw new UnsupportedOperationException("Only file:// URIs are supported");
}
final String originPath = packageURI.getPath();
try {
mPM.installPackageAsUser(originPath, observer.getBinder(), flags, installerPackageName,
mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
@Override
public int installExistingPackage(String packageName) throws NameNotFoundException {
return installExistingPackage(packageName, PackageManager.INSTALL_REASON_UNKNOWN);

View File

@@ -26,7 +26,6 @@ import android.content.pm.ChangedPackages;
import android.content.pm.InstantAppInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.IDexModuleRegisterCallback;
import android.content.pm.IPackageInstallObserver2;
import android.content.pm.IPackageInstaller;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageDeleteObserver2;
@@ -222,13 +221,6 @@ interface IPackageManager {
ParceledListSlice queryInstrumentation(
String targetPackage, int flags);
/** @deprecated Use PackageInstaller instead */
void installPackageAsUser(in String originPath,
in IPackageInstallObserver2 observer,
int flags,
in String installerPackageName,
int userId);
void finishPackageInstall(int token, boolean didLaunch);
void setInstallerPackageName(in String targetPackage, in String installerPackageName);

View File

@@ -324,7 +324,14 @@ public class PackageInstaller {
*/
public int createSession(@NonNull SessionParams params) throws IOException {
try {
return mInstaller.createSession(params, mInstallerPackageName, mUserId);
final String installerPackage;
if (params.installerPackageName == null) {
installerPackage = mInstallerPackageName;
} else {
installerPackage = params.installerPackageName;
}
return mInstaller.createSession(params, installerPackage, mUserId);
} catch (RuntimeException e) {
ExceptionUtils.maybeUnwrapIOException(e);
throw e;
@@ -1081,6 +1088,8 @@ public class PackageInstaller {
public String volumeUuid;
/** {@hide} */
public String[] grantedRuntimePermissions;
/** {@hide} */
public String installerPackageName;
/**
* Construct parameters for a new package install session.
@@ -1109,6 +1118,7 @@ public class PackageInstaller {
abiOverride = source.readString();
volumeUuid = source.readString();
grantedRuntimePermissions = source.readStringArray();
installerPackageName = source.readString();
}
/**
@@ -1304,6 +1314,18 @@ public class PackageInstaller {
}
}
/**
* Set the installer package for the app.
*
* By default this is the app that created the {@link PackageInstaller} object.
*
* @param installerPackageName name of the installer package
* {@hide}
*/
public void setInstallerPackageName(String installerPackageName) {
this.installerPackageName = installerPackageName;
}
/** {@hide} */
public void dump(IndentingPrintWriter pw) {
pw.printPair("mode", mode);
@@ -1319,6 +1341,7 @@ public class PackageInstaller {
pw.printPair("abiOverride", abiOverride);
pw.printPair("volumeUuid", volumeUuid);
pw.printPair("grantedRuntimePermissions", grantedRuntimePermissions);
pw.printPair("installerPackageName", installerPackageName);
pw.println();
}
@@ -1343,6 +1366,7 @@ public class PackageInstaller {
dest.writeString(abiOverride);
dest.writeString(volumeUuid);
dest.writeStringArray(grantedRuntimePermissions);
dest.writeString(installerPackageName);
}
public static final Parcelable.Creator<SessionParams>

View File

@@ -47,7 +47,6 @@ import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Bundle;
@@ -4717,17 +4716,6 @@ public abstract class PackageManager {
}
}
/**
* @deprecated replaced by {@link PackageInstaller}
* @hide
*/
@Deprecated
public abstract void installPackage(
Uri packageURI,
PackageInstallObserver observer,
@InstallFlags int flags,
String installerPackageName);
/**
* If there is already an application with the given package name installed
* on the system for other users, also install it for the calling user.

View File

@@ -66,7 +66,6 @@ public class FullRestoreEngine extends RestoreEngine {
// Task in charge of monitoring timeouts
private final BackupRestoreTask mMonitorTask;
private final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver();
private final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver();
// Dedicated observer, if any
@@ -249,13 +248,12 @@ public class FullRestoreEngine extends RestoreEngine {
Slog.d(TAG, "APK file; installing");
}
// Try to install the app.
String installerName = mPackageInstallers.get(pkg);
String installerPackageName = mPackageInstallers.get(pkg);
boolean isSuccessfullyInstalled = RestoreUtils.installApk(
instream, mBackupManagerService.getPackageManager(),
mInstallObserver, mDeleteObserver, mManifestSignatures,
mPackagePolicies, info, installerName,
bytesReadListener, mBackupManagerService.getDataDir()
);
instream, mBackupManagerService.getContext(),
mDeleteObserver, mManifestSignatures,
mPackagePolicies, info, installerPackageName,
bytesReadListener);
// good to go; promote to ACCEPT
mPackagePolicies.put(pkg, isSuccessfullyInstalled
? RestorePolicy.ACCEPT

View File

@@ -88,7 +88,6 @@ public class PerformAdbRestoreTask implements Runnable {
private final String mDecryptPassword;
private final AtomicBoolean mLatchObject;
private final PackageManagerBackupAgent mPackageManagerBackupAgent;
private final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver();
private final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver();
private IFullBackupRestoreObserver mObserver;
@@ -513,13 +512,11 @@ public class PerformAdbRestoreTask implements Runnable {
Slog.d(TAG, "APK file; installing");
}
// Try to install the app.
String installerName = mPackageInstallers.get(pkg);
boolean isSuccessfullyInstalled = RestoreUtils.installApk(
instream, mBackupManagerService.getPackageManager(),
mInstallObserver, mDeleteObserver, mManifestSignatures,
mPackagePolicies, info, installerName,
bytesReadListener, mBackupManagerService.getDataDir()
);
String installerPackageName = mPackageInstallers.get(pkg);
boolean isSuccessfullyInstalled = RestoreUtils.installApk(instream,
mBackupManagerService.getContext(),
mDeleteObserver, mManifestSignatures, mPackagePolicies,
info, installerPackageName, bytesReadListener);
// good to go; promote to ACCEPT
mPackagePolicies.put(pkg, isSuccessfullyInstalled
? RestorePolicy.ACCEPT

View File

@@ -1,89 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.server.backup.restore;
import android.app.PackageInstallObserver;
import android.os.Bundle;
import com.android.internal.annotations.GuardedBy;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Synchronous implementation of PackageInstallObserver.
*
* Allows the caller to synchronously wait for package install event.
*/
public class RestoreInstallObserver extends PackageInstallObserver {
@GuardedBy("mDone")
private final AtomicBoolean mDone = new AtomicBoolean();
private String mPackageName;
private int mResult;
public RestoreInstallObserver() {
}
/**
* Resets the observer to prepare for another installation.
*/
public void reset() {
synchronized (mDone) {
mDone.set(false);
}
}
/**
* Synchronously waits for completion.
*/
public void waitForCompletion() {
synchronized (mDone) {
while (mDone.get() == false) {
try {
mDone.wait();
} catch (InterruptedException e) {
}
}
}
}
/**
* Returns result code.
*/
public int getResult() {
return mResult;
}
/**
* Returns installed package name.
*/
public String getPackageName() {
return mPackageName;
}
@Override
public void onPackageInstalled(String packageName, int returnCode,
String msg, Bundle extras) {
synchronized (mDone) {
mResult = returnCode;
mPackageName = packageName;
mDone.set(true);
mDone.notifyAll();
}
}
}

View File

@@ -19,23 +19,31 @@ package com.android.server.backup.utils;
import static com.android.server.backup.RefactoredBackupManagerService.DEBUG;
import static com.android.server.backup.RefactoredBackupManagerService.TAG;
import android.content.Context;
import android.content.IIntentReceiver;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.Session;
import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Process;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.server.backup.FileMetadata;
import com.android.server.backup.restore.RestoreDeleteObserver;
import com.android.server.backup.restore.RestoreInstallObserver;
import com.android.server.backup.restore.RestorePolicy;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
/**
@@ -47,62 +55,66 @@ public class RestoreUtils {
* Reads apk contents from input stream and installs the apk.
*
* @param instream - input stream to read apk data from.
* @param packageManager - {@link PackageManager} instance.
* @param installObserver - {@link RestoreInstallObserver} instance.
* @param context - installing context
* @param deleteObserver - {@link RestoreDeleteObserver} instance.
* @param manifestSignatures - manifest signatures.
* @param packagePolicies - package policies.
* @param info - backup file info.
* @param installerPackage - installer package.
* @param installerPackageName - package name of installer.
* @param bytesReadListener - listener to be called for counting bytes read.
* @param dataDir - directory where to create apk file.
* @return true if apk was successfully read and installed and false otherwise.
*/
// TODO: Refactor to get rid of unneeded params.
public static boolean installApk(InputStream instream, PackageManager packageManager,
RestoreInstallObserver installObserver, RestoreDeleteObserver deleteObserver,
public static boolean installApk(InputStream instream, Context context,
RestoreDeleteObserver deleteObserver,
HashMap<String, Signature[]> manifestSignatures,
HashMap<String, RestorePolicy> packagePolicies,
FileMetadata info,
String installerPackage, BytesReadListener bytesReadListener,
File dataDir) {
String installerPackageName, BytesReadListener bytesReadListener) {
boolean okay = true;
if (DEBUG) {
Slog.d(TAG, "Installing from backup: " + info.packageName);
}
// The file content is an .apk file. Copy it out to a staging location and
// attempt to install it.
File apkFile = new File(dataDir, info.packageName);
try {
FileOutputStream apkStream = new FileOutputStream(apkFile);
byte[] buffer = new byte[32 * 1024];
long size = info.size;
while (size > 0) {
long toRead = (buffer.length < size) ? buffer.length : size;
int didRead = instream.read(buffer, 0, (int) toRead);
if (didRead >= 0) {
bytesReadListener.onBytesRead(didRead);
LocalIntentReceiver receiver = new LocalIntentReceiver();
PackageManager packageManager = context.getPackageManager();
PackageInstaller installer = packageManager.getPackageInstaller();
SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
params.setInstallerPackageName(installerPackageName);
int sessionId = installer.createSession(params);
try {
try (Session session = installer.openSession(sessionId)) {
try (OutputStream apkStream = session.openWrite(info.packageName, 0,
info.size)) {
byte[] buffer = new byte[32 * 1024];
long size = info.size;
while (size > 0) {
long toRead = (buffer.length < size) ? buffer.length : size;
int didRead = instream.read(buffer, 0, (int) toRead);
if (didRead >= 0) {
bytesReadListener.onBytesRead(didRead);
}
apkStream.write(buffer, 0, didRead);
size -= didRead;
}
}
session.commit(receiver.getIntentSender());
}
apkStream.write(buffer, 0, didRead);
size -= didRead;
} catch (Exception t) {
installer.abandonSession(sessionId);
throw t;
}
apkStream.close();
// make sure the installer can read it
apkFile.setReadable(true, false);
Intent result = receiver.getResult();
int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
PackageInstaller.STATUS_FAILURE);
// Now install it
Uri packageUri = Uri.fromFile(apkFile);
installObserver.reset();
// TODO: PackageManager.installPackage() is deprecated, refactor.
packageManager.installPackage(packageUri, installObserver,
PackageManager.INSTALL_REPLACE_EXISTING | PackageManager.INSTALL_FROM_ADB,
installerPackage);
installObserver.waitForCompletion();
if (installObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) {
if (status != PackageInstaller.STATUS_SUCCESS) {
// The only time we continue to accept install of data even if the
// apk install failed is if we had already determined that we could
// accept the data regardless.
@@ -112,10 +124,12 @@ public class RestoreUtils {
} else {
// Okay, the install succeeded. Make sure it was the right app.
boolean uninstall = false;
if (!installObserver.getPackageName().equals(info.packageName)) {
final String installedPackageName = result.getStringExtra(
PackageInstaller.EXTRA_PACKAGE_NAME);
if (!installedPackageName.equals(info.packageName)) {
Slog.w(TAG, "Restore stream claimed to include apk for "
+ info.packageName + " but apk was really "
+ installObserver.getPackageName());
+ installedPackageName);
// delete the package we just put in place; it might be fraudulent
okay = false;
uninstall = true;
@@ -161,7 +175,7 @@ public class RestoreUtils {
if (uninstall) {
deleteObserver.reset();
packageManager.deletePackage(
installObserver.getPackageName(),
installedPackageName,
deleteObserver, 0);
deleteObserver.waitForCompletion();
}
@@ -169,10 +183,44 @@ public class RestoreUtils {
} catch (IOException e) {
Slog.e(TAG, "Unable to transcribe restored apk for install");
okay = false;
} finally {
apkFile.delete();
}
return okay;
}
private static class LocalIntentReceiver {
private final Object mLock = new Object();
@GuardedBy("mLock")
private Intent mResult = null;
private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
@Override
public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
synchronized (mLock) {
mResult = intent;
mLock.notifyAll();
}
}
};
public IntentSender getIntentSender() {
return new IntentSender((IIntentSender) mLocalSender);
}
public Intent getResult() {
synchronized (mLock) {
while (mResult == null) {
try {
mLock.wait();
} catch (InterruptedException e) {
// ignored
}
}
return mResult;
}
}
}
}

View File

@@ -415,7 +415,12 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
params.installFlags |= PackageManager.INSTALL_FROM_ADB;
} else {
mAppOps.checkPackage(callingUid, installerPackageName);
// Only apps with INSTALL_PACKAGES are allowed to set an installer that is not the
// caller.
if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES) !=
PackageManager.PERMISSION_GRANTED) {
mAppOps.checkPackage(callingUid, installerPackageName);
}
params.installFlags &= ~PackageManager.INSTALL_FROM_ADB;
params.installFlags &= ~PackageManager.INSTALL_ALL_USERS;

View File

@@ -346,13 +346,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|| (isSelfUpdatePermissionGranted
&& mPm.getPackageUid(mPackageName, 0, userId) == mInstallerUid);
final boolean isInstallerRoot = (mInstallerUid == Process.ROOT_UID);
final boolean isInstallerSystem = (mInstallerUid == Process.SYSTEM_UID);
final boolean forcePermissionPrompt =
(params.installFlags & PackageManager.INSTALL_FORCE_PERMISSION_PROMPT) != 0;
// Device owners and affiliated profile owners are allowed to silently install packages, so
// the permission check is waived if the installer is the device owner.
return forcePermissionPrompt || !(isPermissionGranted || isInstallerRoot
|| isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked());
|| isInstallerSystem || isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked());
}
public PackageInstallerSession(PackageInstallerService.InternalCallback callback,

View File

@@ -13141,80 +13141,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
@Override
public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,
int installFlags, String installerPackageName, int userId) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);
final int callingUid = Binder.getCallingUid();
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, true /* checkShell */, "installPackageAsUser");
if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
try {
if (observer != null) {
observer.onPackageInstalled("", INSTALL_FAILED_USER_RESTRICTED, null, null);
}
} catch (RemoteException re) {
}
return;
}
if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {
installFlags |= PackageManager.INSTALL_FROM_ADB;
} else {
// Caller holds INSTALL_PACKAGES permission, so we're less strict
// about installerPackageName.
installFlags &= ~PackageManager.INSTALL_FROM_ADB;
installFlags &= ~PackageManager.INSTALL_ALL_USERS;
}
UserHandle user;
if ((installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
user = UserHandle.ALL;
} else {
user = new UserHandle(userId);
}
// Only system components can circumvent runtime permissions when installing.
if ((installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0
&& mContext.checkCallingOrSelfPermission(Manifest.permission
.INSTALL_GRANT_RUNTIME_PERMISSIONS) == PackageManager.PERMISSION_DENIED) {
throw new SecurityException("You need the "
+ "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission "
+ "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag");
}
if ((installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0
|| (installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
throw new IllegalArgumentException(
"New installs into ASEC containers no longer supported");
}
final File originFile = new File(originPath);
final OriginInfo origin = OriginInfo.fromUntrustedFile(originFile);
final Message msg = mHandler.obtainMessage(INIT_COPY);
final VerificationInfo verificationInfo = new VerificationInfo(
null /*originatingUri*/, null /*referrer*/, -1 /*originatingUid*/, callingUid);
final InstallParams params = new InstallParams(origin, null /*moveInfo*/, observer,
installFlags, installerPackageName, null /*volumeUuid*/, verificationInfo, user,
null /*packageAbiOverride*/, null /*grantedPermissions*/,
null /*certificates*/, PackageManager.INSTALL_REASON_UNKNOWN);
params.setTraceMethod("installAsUser").setTraceCookie(System.identityHashCode(params));
msg.obj = params;
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installAsUser",
System.identityHashCode(msg.obj));
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
System.identityHashCode(msg.obj));
mHandler.sendMessage(msg);
}
/**
* Ensure that the install reason matches what we know about the package installer (e.g. whether
* it is acting on behalf on an enterprise or the user).

View File

@@ -1,6 +1,5 @@
package com.android.server.backup.testutils;
import android.app.PackageInstallObserver;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
@@ -32,7 +31,6 @@ import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Handler;
import android.os.UserHandle;
import android.os.storage.VolumeInfo;
@@ -621,12 +619,6 @@ public class PackageManagerStub extends PackageManager {
return null;
}
@Override
public void installPackage(Uri packageURI, PackageInstallObserver observer, int flags,
String installerPackageName) {
}
@Override
public int installExistingPackage(String packageName)
throws NameNotFoundException {

View File

@@ -1086,15 +1086,6 @@ public class MockPackageManager extends PackageManager {
throw new UnsupportedOperationException();
}
/**
* @hide
*/
@Override
public void installPackage(Uri packageURI, PackageInstallObserver observer,
int flags, String installerPackageName) {
throw new UnsupportedOperationException();
}
/**
* @hide
*/

View File

@@ -71,18 +71,6 @@ public class PmPermissionsTests extends AndroidTestCase {
private class TestInstallObserver extends PackageInstallObserver {
}
@SmallTest
public void testInstallPackage() {
TestInstallObserver observer = new TestInstallObserver();
try {
mPm.installPackage(null, observer, 0, null);
fail("PackageManager.installPackage" +
"did not throw SecurityException as expected");
} catch (SecurityException e) {
// expected
}
}
/*
* This test verifies that PackageManger.freeStorage
* enforces permission android.permission.CLEAR_APP_CACHE