1 /* 2 * Copyright (C) 2019 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 17 package com.android.cts.rollback.lib; 18 19 import android.content.BroadcastReceiver; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.IntentFilter; 23 import android.content.pm.PackageInfo; 24 import android.content.pm.PackageInstaller; 25 import android.content.pm.PackageManager; 26 import android.content.pm.VersionedPackage; 27 import android.content.rollback.PackageRollbackInfo; 28 import android.content.rollback.RollbackInfo; 29 import android.content.rollback.RollbackManager; 30 31 import androidx.test.InstrumentationRegistry; 32 33 import java.io.IOException; 34 import java.util.ArrayList; 35 import java.util.List; 36 import java.util.concurrent.BlockingQueue; 37 import java.util.concurrent.LinkedBlockingQueue; 38 39 /** 40 * Utilities to facilitate testing rollbacks. 41 */ 42 public class Utils { 43 /** 44 * Returns the version of the given package installed on device. 45 * Returns -1 if the package is not currently installed. 46 */ getInstalledVersion(String packageName)47 public static long getInstalledVersion(String packageName) { 48 Context context = InstrumentationRegistry.getContext(); 49 PackageManager pm = context.getPackageManager(); 50 try { 51 PackageInfo info = pm.getPackageInfo(packageName, PackageManager.MATCH_APEX); 52 return info.getLongVersionCode(); 53 } catch (PackageManager.NameNotFoundException e) { 54 return -1; 55 } 56 } 57 58 /** 59 * Gets the RollbackManager for the instrumentation context. 60 */ getRollbackManager()61 public static RollbackManager getRollbackManager() { 62 Context context = InstrumentationRegistry.getContext(); 63 RollbackManager rm = (RollbackManager) context.getSystemService(Context.ROLLBACK_SERVICE); 64 if (rm == null) { 65 throw new AssertionError("Failed to get RollbackManager"); 66 } 67 return rm; 68 } 69 70 /** 71 * Returns a rollback for the given package name in the list of 72 * rollbacks. Returns null if there are no available rollbacks, and throws 73 * an assertion if there is more than one. 74 */ getRollback(List<RollbackInfo> rollbacks, String packageName)75 private static RollbackInfo getRollback(List<RollbackInfo> rollbacks, String packageName) { 76 RollbackInfo found = null; 77 for (RollbackInfo rollback : rollbacks) { 78 for (PackageRollbackInfo info : rollback.getPackages()) { 79 if (packageName.equals(info.getPackageName())) { 80 if (found != null) { 81 throw new AssertionError("Multiple available matching rollbacks found"); 82 } 83 found = rollback; 84 break; 85 } 86 } 87 } 88 return found; 89 } 90 91 /** 92 * Returns a rollback for the given rollback Id, if found. Otherwise, returns null. 93 */ getRollbackById(List<RollbackInfo> rollbacks, int rollbackId)94 private static RollbackInfo getRollbackById(List<RollbackInfo> rollbacks, int rollbackId) { 95 for (RollbackInfo rollback :rollbacks) { 96 if (rollback.getRollbackId() == rollbackId) { 97 return rollback; 98 } 99 } 100 return null; 101 } 102 103 /** 104 * Returns an available rollback for the given package name. Returns null 105 * if there are no available rollbacks, and throws an assertion if there 106 * is more than one. 107 */ getAvailableRollback(String packageName)108 public static RollbackInfo getAvailableRollback(String packageName) { 109 RollbackManager rm = getRollbackManager(); 110 return getRollback(rm.getAvailableRollbacks(), packageName); 111 } 112 113 /** 114 * Returns a recently committed rollback for the given package name. Returns null 115 * if there are no available rollbacks, and throws an assertion if there 116 * is more than one. 117 */ getCommittedRollback(String packageName)118 public static RollbackInfo getCommittedRollback(String packageName) { 119 RollbackManager rm = getRollbackManager(); 120 return getRollback(rm.getRecentlyCommittedRollbacks(), packageName); 121 } 122 123 /** 124 * Returns a recently committed rollback for the given rollback Id. 125 * Returns null if no committed rollback with a matching Id was found. 126 */ getCommittedRollbackById(int rollbackId)127 public static RollbackInfo getCommittedRollbackById(int rollbackId) { 128 RollbackManager rm = getRollbackManager(); 129 return getRollbackById(rm.getRecentlyCommittedRollbacks(), rollbackId); 130 } 131 132 /** 133 * Uninstalls the given package. 134 * Does nothing if the package is not installed. 135 * @throws AssertionError if package can't be uninstalled. 136 */ uninstall(String packageName)137 public static void uninstall(String packageName) throws InterruptedException, IOException { 138 // No need to uninstall if the package isn't installed. 139 if (getInstalledVersion(packageName) == -1) { 140 return; 141 } 142 143 Context context = InstrumentationRegistry.getContext(); 144 PackageManager packageManager = context.getPackageManager(); 145 PackageInstaller packageInstaller = packageManager.getPackageInstaller(); 146 packageInstaller.uninstall(packageName, LocalIntentSender.getIntentSender()); 147 Intent result = LocalIntentSender.getIntentSenderResult(); 148 int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, 149 PackageInstaller.STATUS_FAILURE); 150 if (status == -1) { 151 throw new AssertionError("PENDING USER ACTION"); 152 } else if (status > 0) { 153 String message = result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE); 154 throw new AssertionError(message == null ? "UNKNOWN FAILURE" : message); 155 } 156 } 157 158 /** 159 * Commit the given rollback. 160 * @throws AssertionError if the rollback fails. 161 */ rollback(int rollbackId, TestApp... causePackages)162 public static void rollback(int rollbackId, TestApp... causePackages) 163 throws InterruptedException { 164 List<VersionedPackage> causes = new ArrayList<>(); 165 for (TestApp cause : causePackages) { 166 causes.add(cause.getVersionedPackage()); 167 } 168 169 RollbackManager rm = getRollbackManager(); 170 rm.commitRollback(rollbackId, causes, LocalIntentSender.getIntentSender()); 171 Intent result = LocalIntentSender.getIntentSenderResult(); 172 int status = result.getIntExtra(RollbackManager.EXTRA_STATUS, 173 RollbackManager.STATUS_FAILURE); 174 if (status != RollbackManager.STATUS_SUCCESS) { 175 String message = result.getStringExtra(RollbackManager.EXTRA_STATUS_MESSAGE); 176 throw new AssertionError(message); 177 } 178 } 179 180 /** 181 * Waits for the given session to be marked as ready. 182 * Throws an assertion if the session fails. 183 */ waitForSessionReady(int sessionId)184 public static void waitForSessionReady(int sessionId) { 185 BlockingQueue<PackageInstaller.SessionInfo> sessionStatus = new LinkedBlockingQueue<>(); 186 BroadcastReceiver sessionUpdatedReceiver = new BroadcastReceiver() { 187 @Override 188 public void onReceive(Context context, Intent intent) { 189 PackageInstaller.SessionInfo info = 190 intent.getParcelableExtra(PackageInstaller.EXTRA_SESSION); 191 if (info != null && info.getSessionId() == sessionId) { 192 if (info.isStagedSessionReady() || info.isStagedSessionFailed()) { 193 try { 194 sessionStatus.put(info); 195 } catch (InterruptedException e) { 196 throw new AssertionError(e); 197 } 198 } 199 } 200 } 201 }; 202 IntentFilter sessionUpdatedFilter = 203 new IntentFilter(PackageInstaller.ACTION_SESSION_UPDATED); 204 205 Context context = InstrumentationRegistry.getContext(); 206 context.registerReceiver(sessionUpdatedReceiver, sessionUpdatedFilter); 207 208 PackageInstaller installer = context.getPackageManager().getPackageInstaller(); 209 PackageInstaller.SessionInfo info = installer.getSessionInfo(sessionId); 210 211 try { 212 if (info.isStagedSessionReady() || info.isStagedSessionFailed()) { 213 sessionStatus.put(info); 214 } 215 216 info = sessionStatus.take(); 217 context.unregisterReceiver(sessionUpdatedReceiver); 218 if (info.isStagedSessionFailed()) { 219 throw new AssertionError(info.getStagedSessionErrorMessage()); 220 } 221 } catch (InterruptedException e) { 222 throw new AssertionError(e); 223 } 224 } 225 } 226 227