blob: e10e278988910d22df93f7980376a22a6bc0a58b [file] [log] [blame]
/*
* 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 android.packageinstaller.externalsources.cts;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.MediumTest;
import android.support.test.runner.AndroidJUnit4;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.BySelector;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject2;
import android.support.test.uiautomator.Until;
import android.support.v4.content.FileProvider;
import android.util.Log;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class ExternalSourcesTest {
private static final String TAG = ExternalSourcesTest.class.getSimpleName();
private static final String TEST_APK_NAME = "CtsEmptyTestApp.apk";
private static final String TEST_APK_EXTERNAL_LOCATION = "/data/local/tmp/cts/externalsources";
private static final String CONTENT_AUTHORITY =
"android.packageinstaller.externalsources.cts.fileprovider";
private static final String PACKAGE_INSTALLER_PACKAGE_NAME = "com.android.packageinstaller";
private static final String INSTALL_CONFIRM_TEXT_ID = "install_confirm_question";
private static final String WM_DISMISS_KEYGUARD_COMMAND = "wm dismiss-keyguard";
private static final String ALERT_DIALOG_TITLE_ID = "android:id/alertTitle";
private static final long WAIT_FOR_UI_TIMEOUT = 5000;
private Context mContext;
private PackageManager mPm;
private Instrumentation mInstrumentation;
private String mPackageName;
private File mApkFile;
private UiDevice mUiDevice;
@BeforeClass
public static void setUpOnce() throws IOException {
File srcApkFile = new File(TEST_APK_EXTERNAL_LOCATION, TEST_APK_NAME);
File destApkFile = new File(InstrumentationRegistry.getTargetContext().getFilesDir(),
TEST_APK_NAME);
copyFile(srcApkFile, destApkFile);
}
@Before
public void setUp() throws Exception {
mContext = InstrumentationRegistry.getTargetContext();
mInstrumentation = InstrumentationRegistry.getInstrumentation();
mPm = mContext.getPackageManager();
mPackageName = mContext.getPackageName();
mApkFile = new File(mContext.getFilesDir(), TEST_APK_NAME);
mUiDevice = UiDevice.getInstance(mInstrumentation);
if (!mUiDevice.isScreenOn()) {
mUiDevice.wakeUp();
}
mUiDevice.executeShellCommand(WM_DISMISS_KEYGUARD_COMMAND);
}
private static void copyFile(File srcFile, File destFile) throws IOException {
if (destFile.exists()) {
destFile.delete();
}
FileInputStream inputStream = new FileInputStream(srcFile);
FileOutputStream out = new FileOutputStream(destFile);
try {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) >= 0) {
out.write(buffer, 0, bytesRead);
}
Log.d(TAG, "copied file " + srcFile + " to " + destFile);
} finally {
out.flush();
try {
out.getFD().sync();
} catch (IOException e) {
}
out.close();
inputStream.close();
}
}
private void launchPackageInstaller() {
Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.setData(FileProvider.getUriForFile(mContext, CONTENT_AUTHORITY, mApkFile));
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Log.d(TAG, "Starting intent with uri " + intent.getDataString());
mContext.startActivity(intent);
}
private void assertInstallAllowed(String errorMessage) {
BySelector selector = By.res(PACKAGE_INSTALLER_PACKAGE_NAME, INSTALL_CONFIRM_TEXT_ID);
UiObject2 uiObject = mUiDevice.wait(Until.findObject(selector), WAIT_FOR_UI_TIMEOUT);
Assert.assertNotNull(errorMessage, uiObject);
mUiDevice.pressBack();
}
private void assertInstallBlocked(String errorMessage) {
BySelector selector = By.res(ALERT_DIALOG_TITLE_ID);
UiObject2 settingsButton = mUiDevice.wait(Until.findObject(selector), WAIT_FOR_UI_TIMEOUT);
Assert.assertNotNull(errorMessage, settingsButton);
mUiDevice.pressBack();
}
private void setAppOpsMode(String mode) throws IOException {
final StringBuilder commandBuilder = new StringBuilder("appops set");
commandBuilder.append(" " + mPackageName);
commandBuilder.append(" REQUEST_INSTALL_PACKAGES");
commandBuilder.append(" " + mode);
mUiDevice.executeShellCommand(commandBuilder.toString());
}
@Test
public void blockedSourceTest() throws Exception {
setAppOpsMode("deny");
boolean isTrusted = mPm.canRequestPackageInstalls();
Assert.assertFalse("Package " + mPackageName
+ " allowed to install packages after setting app op to errored", isTrusted);
launchPackageInstaller();
assertInstallBlocked("Install blocking dialog not shown when app op set to errored");
}
@Test
public void allowedSourceTest() throws Exception {
setAppOpsMode("allow");
boolean isTrusted = mPm.canRequestPackageInstalls();
Assert.assertTrue("Package " + mPackageName
+ " blocked from installing packages after setting app op to allowed", isTrusted);
launchPackageInstaller();
assertInstallAllowed("Install confirmation not shown when app op set to allowed");
}
@Test
public void defaultSourceTest() throws Exception {
boolean isTrusted = mPm.canRequestPackageInstalls();
Assert.assertFalse("Package " + mPackageName
+ " with default app ops state allowed to install packages", isTrusted);
launchPackageInstaller();
assertInstallBlocked("Install blocking dialog not shown when app op set to default");
}
@After
public void tearDown() throws Exception {
mUiDevice.pressHome();
setAppOpsMode("default");
}
}