1 /* 2 * Copyright (C) 2015 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 package android.packageinstaller.admin.cts; 17 18 import static org.junit.Assert.assertEquals; 19 import static org.junit.Assert.assertFalse; 20 import static org.junit.Assert.assertNotNull; 21 import static org.junit.Assert.assertTrue; 22 23 import android.app.Instrumentation; 24 import android.app.PendingIntent; 25 import android.app.UiAutomation; 26 import android.app.admin.DevicePolicyManager; 27 import android.content.BroadcastReceiver; 28 import android.content.ComponentName; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.content.IntentFilter; 32 import android.content.IntentSender; 33 import android.content.pm.PackageInfo; 34 import android.content.pm.PackageInstaller; 35 import android.content.pm.PackageManager; 36 import android.os.ParcelFileDescriptor; 37 import android.os.Process; 38 import android.util.Log; 39 40 import androidx.test.platform.app.InstrumentationRegistry; 41 import androidx.test.uiautomator.UiDevice; 42 43 import com.android.bedstead.nene.TestApis; 44 import com.android.compatibility.common.util.RequiredFeatureRule; 45 46 import org.junit.After; 47 import org.junit.AssumptionViolatedException; 48 import org.junit.Before; 49 import org.junit.Rule; 50 import org.junit.rules.TestRule; 51 import org.junit.runners.model.Statement; 52 53 import java.io.BufferedReader; 54 import java.io.File; 55 import java.io.FileInputStream; 56 import java.io.InputStream; 57 import java.io.InputStreamReader; 58 import java.io.OutputStream; 59 import java.util.ArrayList; 60 61 /** 62 * Base test case for testing PackageInstaller. 63 */ 64 public class BasePackageInstallTest { 65 private static final String TAG = BasePackageInstallTest.class.getSimpleName(); 66 protected static final String TEST_APP_LOCATION = 67 "/data/local/tmp/cts/packageinstaller/CtsEmptyTestApp.apk"; 68 protected static final String TEST_APP_PKG = "android.packageinstaller.emptytestapp.cts"; 69 protected static final int PACKAGE_INSTALLER_TIMEOUT_MS = 60000; // 60 seconds 70 private static final String ACTION_INSTALL_COMMIT = 71 "com.android.cts.deviceowner.INTENT_PACKAGE_INSTALL_COMMIT"; 72 protected static final int PACKAGE_INSTALLER_STATUS_UNDEFINED = -1000; 73 public static final String PACKAGE_NAME = SilentPackageInstallTest.class.getPackage().getName(); 74 75 protected Context mContext; 76 protected UiDevice mDevice; 77 protected DevicePolicyManager mDevicePolicyManager; 78 protected PackageManager mPackageManager; 79 private PackageInstaller mPackageInstaller; 80 private PackageInstaller.Session mSession; 81 protected boolean mCallbackReceived; 82 protected int mCallbackStatus; 83 protected Intent mCallbackIntent; 84 protected ComponentName mDeviceOwner; 85 86 protected Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation(); 87 protected UiAutomation mUiAutomation = mInstrumentation.getUiAutomation(); 88 protected final Object mPackageInstallerTimeoutLock = new Object(); 89 90 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 91 @Override 92 public void onReceive(Context context, Intent intent) { 93 synchronized (mPackageInstallerTimeoutLock) { 94 mCallbackStatus = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, 95 PACKAGE_INSTALLER_STATUS_UNDEFINED); 96 if (mCallbackStatus == PackageInstaller.STATUS_SUCCESS) { 97 mContext.unregisterReceiver(this); 98 assertEquals(TEST_APP_PKG, intent.getStringExtra( 99 PackageInstaller.EXTRA_PACKAGE_NAME)); 100 } else if (mCallbackStatus == PackageInstaller.STATUS_PENDING_USER_ACTION) { 101 mCallbackIntent = (Intent) intent.getExtras().get(Intent.EXTRA_INTENT); 102 } 103 mCallbackReceived = true; 104 mPackageInstallerTimeoutLock.notify(); 105 } 106 } 107 }; 108 109 @Rule 110 public RequiredFeatureRule mAdminFeatureRule = new RequiredFeatureRule( 111 PackageManager.FEATURE_DEVICE_ADMIN); 112 113 @Rule 114 public TestRule mDeviceOwnerRule = (base, description) -> new Statement() { 115 @Override 116 public void evaluate() throws Throwable { 117 try { 118 addDeviceOwner(); 119 base.evaluate(); 120 } finally { 121 removeDeviceOwner(); 122 } 123 } 124 }; 125 126 @Before setUp()127 public void setUp() throws Exception { 128 mContext = mInstrumentation.getContext(); 129 mDevice = UiDevice.getInstance(mInstrumentation); 130 mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class); 131 mPackageManager = mContext.getPackageManager(); 132 mPackageInstaller = mPackageManager.getPackageInstaller(); 133 assertNotNull(mPackageInstaller); 134 135 forceUninstall(); 136 // check that app is not already installed 137 assertFalse(isPackageInstalled(TEST_APP_PKG)); 138 } 139 addDeviceOwner()140 private void addDeviceOwner() { 141 mDeviceOwner = new ComponentName(mInstrumentation.getContext(), BasicAdminReceiver.class); 142 try { 143 TestApis.devicePolicy().setDeviceOwner(mDeviceOwner); 144 } catch (Exception e) { 145 Log.e(TAG, e.getMessage()); 146 throw new AssumptionViolatedException( 147 "Could not set BasicAdminReceiver.class as device owner"); 148 } 149 } 150 151 @After tearDown()152 public void tearDown() throws Exception { 153 if (mDevicePolicyManager.isDeviceOwnerApp(PACKAGE_NAME) || 154 mDevicePolicyManager.isProfileOwnerApp(PACKAGE_NAME)) { 155 mDevicePolicyManager.setUninstallBlocked(mDeviceOwner, TEST_APP_PKG, false); 156 } 157 try { 158 mContext.unregisterReceiver(mBroadcastReceiver); 159 } catch (IllegalArgumentException e) { 160 // ignore 161 } 162 if (mSession != null) { 163 mSession.abandon(); 164 } 165 forceUninstall(); 166 } 167 removeDeviceOwner()168 private void removeDeviceOwner() { 169 try { 170 TestApis.devicePolicy().getDeviceOwner().remove(); 171 } catch (NullPointerException e) { 172 Log.e(TAG, "Could not find a device owner set by this test. " 173 + "Maybe an owner already exists?"); 174 } 175 } 176 assertInstallPackage()177 protected void assertInstallPackage() throws Exception { 178 assertFalse(isPackageInstalled(TEST_APP_PKG)); 179 synchronized (mPackageInstallerTimeoutLock) { 180 mCallbackReceived = false; 181 mCallbackStatus = PACKAGE_INSTALLER_STATUS_UNDEFINED; 182 } 183 installPackage(TEST_APP_LOCATION); 184 synchronized (mPackageInstallerTimeoutLock) { 185 try { 186 mPackageInstallerTimeoutLock.wait(PACKAGE_INSTALLER_TIMEOUT_MS); 187 } catch (InterruptedException e) { 188 } 189 assertTrue(mCallbackReceived); 190 assertEquals(PackageInstaller.STATUS_SUCCESS, mCallbackStatus); 191 } 192 assertTrue(isPackageInstalled(TEST_APP_PKG)); 193 } 194 tryUninstallPackage()195 protected boolean tryUninstallPackage() throws Exception { 196 assertTrue(isPackageInstalled(TEST_APP_PKG)); 197 synchronized (mPackageInstallerTimeoutLock) { 198 mCallbackReceived = false; 199 mCallbackStatus = PACKAGE_INSTALLER_STATUS_UNDEFINED; 200 } 201 mPackageInstaller.uninstall(TEST_APP_PKG, getCommitCallback(0)); 202 synchronized (mPackageInstallerTimeoutLock) { 203 try { 204 mPackageInstallerTimeoutLock.wait(PACKAGE_INSTALLER_TIMEOUT_MS); 205 } catch (InterruptedException e) { 206 } 207 assertTrue(mCallbackReceived); 208 return mCallbackStatus == PackageInstaller.STATUS_SUCCESS; 209 } 210 } 211 installPackage(String packageLocation)212 protected void installPackage(String packageLocation) throws Exception { 213 PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( 214 PackageInstaller.SessionParams.MODE_FULL_INSTALL); 215 params.setAppPackageName(TEST_APP_PKG); 216 int sessionId = mPackageInstaller.createSession(params); 217 mSession = mPackageInstaller.openSession(sessionId); 218 219 File file = new File(packageLocation); 220 InputStream in = new FileInputStream(file); 221 OutputStream out = mSession.openWrite("SilentPackageInstallerTest", 0, file.length()); 222 byte[] buffer = new byte[65536]; 223 int c; 224 while ((c = in.read(buffer)) != -1) { 225 out.write(buffer, 0, c); 226 } 227 mSession.fsync(out); 228 out.close(); 229 mSession.commit(getCommitCallback(sessionId)); 230 mSession.close(); 231 } 232 getCommitCallback(int sessionId)233 private IntentSender getCommitCallback(int sessionId) { 234 // Create an intent-filter and register the receiver 235 String action = ACTION_INSTALL_COMMIT + "." + sessionId; 236 IntentFilter intentFilter = new IntentFilter(); 237 intentFilter.addAction(action); 238 mContext.registerReceiver(mBroadcastReceiver, intentFilter, 239 Context.RECEIVER_EXPORTED); 240 241 // Create a PendingIntent and use it to generate the IntentSender 242 Intent broadcastIntent = new Intent(action).setPackage(mContext.getPackageName()) 243 .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);; 244 PendingIntent pendingIntent = PendingIntent.getBroadcast( 245 mContext, 246 sessionId, 247 broadcastIntent, 248 PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE); 249 return pendingIntent.getIntentSender(); 250 } 251 isPackageInstalled(String packageName)252 protected boolean isPackageInstalled(String packageName) { 253 try { 254 PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0); 255 return pi != null; 256 } catch (PackageManager.NameNotFoundException e) { 257 return false; 258 } 259 } 260 getInstallReason(String packageName)261 protected int getInstallReason(String packageName) { 262 return mPackageManager.getInstallReason(packageName, Process.myUserHandle()); 263 } 264 forceUninstall()265 protected void forceUninstall() throws Exception { 266 runShellCommand("pm uninstall " + TEST_APP_PKG); 267 } 268 runShellCommand(String command)269 public ArrayList<String> runShellCommand(String command) throws Exception { 270 ParcelFileDescriptor pfd = mUiAutomation.executeShellCommand(command); 271 272 ArrayList<String> ret = new ArrayList<>(); 273 // Read the input stream fully. 274 try (BufferedReader r = new BufferedReader( 275 new InputStreamReader(new ParcelFileDescriptor.AutoCloseInputStream(pfd)))) { 276 String line; 277 while ((line = r.readLine()) != null) { 278 ret.add(line); 279 } 280 } 281 return ret; 282 } 283 } 284