blob: 668f641d8e4f5b5ea1855429c5d530d82ae2ceb0 [file] [log] [blame]
/*
* Copyright (C) 2019 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.cts.rollback.lib;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.VersionedPackage;
import android.content.rollback.PackageRollbackInfo;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
import androidx.test.InstrumentationRegistry;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
/**
* Utilities to facilitate testing rollbacks.
*/
public class Utils {
/**
* Returns the version of the given package installed on device.
* Returns -1 if the package is not currently installed.
*/
public static long getInstalledVersion(String packageName) {
Context context = InstrumentationRegistry.getContext();
PackageManager pm = context.getPackageManager();
try {
PackageInfo info = pm.getPackageInfo(packageName, PackageManager.MATCH_APEX);
return info.getLongVersionCode();
} catch (PackageManager.NameNotFoundException e) {
return -1;
}
}
/**
* Gets the RollbackManager for the instrumentation context.
*/
public static RollbackManager getRollbackManager() {
Context context = InstrumentationRegistry.getContext();
RollbackManager rm = (RollbackManager) context.getSystemService(Context.ROLLBACK_SERVICE);
if (rm == null) {
throw new AssertionError("Failed to get RollbackManager");
}
return rm;
}
/**
* Returns a rollback for the given package name in the list of
* rollbacks. Returns null if there are no available rollbacks, and throws
* an assertion if there is more than one.
*/
private static RollbackInfo getRollback(List<RollbackInfo> rollbacks, String packageName) {
RollbackInfo found = null;
for (RollbackInfo rollback : rollbacks) {
for (PackageRollbackInfo info : rollback.getPackages()) {
if (packageName.equals(info.getPackageName())) {
if (found != null) {
throw new AssertionError("Multiple available matching rollbacks found");
}
found = rollback;
break;
}
}
}
return found;
}
/**
* Returns a rollback for the given rollback Id, if found. Otherwise, returns null.
*/
private static RollbackInfo getRollbackById(List<RollbackInfo> rollbacks, int rollbackId) {
for (RollbackInfo rollback :rollbacks) {
if (rollback.getRollbackId() == rollbackId) {
return rollback;
}
}
return null;
}
/**
* Returns an available rollback for the given package name. Returns null
* if there are no available rollbacks, and throws an assertion if there
* is more than one.
*/
public static RollbackInfo getAvailableRollback(String packageName) {
RollbackManager rm = getRollbackManager();
return getRollback(rm.getAvailableRollbacks(), packageName);
}
/**
* Returns a recently committed rollback for the given package name. Returns null
* if there are no available rollbacks, and throws an assertion if there
* is more than one.
*/
public static RollbackInfo getCommittedRollback(String packageName) {
RollbackManager rm = getRollbackManager();
return getRollback(rm.getRecentlyCommittedRollbacks(), packageName);
}
/**
* Returns a recently committed rollback for the given rollback Id.
* Returns null if no committed rollback with a matching Id was found.
*/
public static RollbackInfo getCommittedRollbackById(int rollbackId) {
RollbackManager rm = getRollbackManager();
return getRollbackById(rm.getRecentlyCommittedRollbacks(), rollbackId);
}
/**
* Uninstalls the given package.
* Does nothing if the package is not installed.
* @throws AssertionError if package can't be uninstalled.
*/
public static void uninstall(String packageName) throws InterruptedException, IOException {
// No need to uninstall if the package isn't installed.
if (getInstalledVersion(packageName) == -1) {
return;
}
Context context = InstrumentationRegistry.getContext();
PackageManager packageManager = context.getPackageManager();
PackageInstaller packageInstaller = packageManager.getPackageInstaller();
packageInstaller.uninstall(packageName, LocalIntentSender.getIntentSender());
Intent result = LocalIntentSender.getIntentSenderResult();
int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
PackageInstaller.STATUS_FAILURE);
if (status == -1) {
throw new AssertionError("PENDING USER ACTION");
} else if (status > 0) {
String message = result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
throw new AssertionError(message == null ? "UNKNOWN FAILURE" : message);
}
}
/**
* Commit the given rollback.
* @throws AssertionError if the rollback fails.
*/
public static void rollback(int rollbackId, TestApp... causePackages)
throws InterruptedException {
List<VersionedPackage> causes = new ArrayList<>();
for (TestApp cause : causePackages) {
causes.add(cause.getVersionedPackage());
}
RollbackManager rm = getRollbackManager();
rm.commitRollback(rollbackId, causes, LocalIntentSender.getIntentSender());
Intent result = LocalIntentSender.getIntentSenderResult();
int status = result.getIntExtra(RollbackManager.EXTRA_STATUS,
RollbackManager.STATUS_FAILURE);
if (status != RollbackManager.STATUS_SUCCESS) {
String message = result.getStringExtra(RollbackManager.EXTRA_STATUS_MESSAGE);
throw new AssertionError(message);
}
}
/**
* Waits for the given session to be marked as ready.
* Throws an assertion if the session fails.
*/
public static void waitForSessionReady(int sessionId) {
BlockingQueue<PackageInstaller.SessionInfo> sessionStatus = new LinkedBlockingQueue<>();
BroadcastReceiver sessionUpdatedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
PackageInstaller.SessionInfo info =
intent.getParcelableExtra(PackageInstaller.EXTRA_SESSION);
if (info != null && info.getSessionId() == sessionId) {
if (info.isStagedSessionReady() || info.isStagedSessionFailed()) {
try {
sessionStatus.put(info);
} catch (InterruptedException e) {
throw new AssertionError(e);
}
}
}
}
};
IntentFilter sessionUpdatedFilter =
new IntentFilter(PackageInstaller.ACTION_SESSION_UPDATED);
Context context = InstrumentationRegistry.getContext();
context.registerReceiver(sessionUpdatedReceiver, sessionUpdatedFilter);
PackageInstaller installer = context.getPackageManager().getPackageInstaller();
PackageInstaller.SessionInfo info = installer.getSessionInfo(sessionId);
try {
if (info.isStagedSessionReady() || info.isStagedSessionFailed()) {
sessionStatus.put(info);
}
info = sessionStatus.take();
context.unregisterReceiver(sessionUpdatedReceiver);
if (info.isStagedSessionFailed()) {
throw new AssertionError(info.getStagedSessionErrorMessage());
}
} catch (InterruptedException e) {
throw new AssertionError(e);
}
}
}