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