summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Babak <babakbo@google.com> 2020-04-17 00:19:52 -0700
committer Babak <babakbo@google.com> 2020-04-17 14:52:06 -0700
commit151a5643bd75775f88df2722daab3a1042f52699 (patch)
tree38bb976c4f5783a52be3f2b0f4157db8fb77e61c
parent0fcd767d5f547d1c2adc0ea4d22ada36415b5527 (diff)
Add Sideloaded detector
A helper class to detect if any package is unsafe or sideloaded. A package is considered unsafe if not a system app and not installed through trusted sources. The usage will be added in the following cls. Bug: 154263570 Test: Added UnitTest Change-Id: Ifffbe64ae95029427aeca4a997bc440dbdc2d3d6
-rw-r--r--packages/CarSystemUI/res/values/config.xml5
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/sideloaded/car/CarSideLoadedAppDetector.java136
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/sideloaded/car/CarSideLoadedAppDetectorTest.java164
3 files changed, 305 insertions, 0 deletions
diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml
index bf1bf38cdb92..5679306b3001 100644
--- a/packages/CarSystemUI/res/values/config.xml
+++ b/packages/CarSystemUI/res/values/config.xml
@@ -76,6 +76,11 @@
<item>com.android.systemui.car.userswitcher.FullscreenUserSwitcherViewMediator</item>
</string-array>
+ <!-- List of package names that are allowed sources of app installation. -->
+ <string-array name="config_allowedAppInstallSources" translatable="false">
+ <item>com.android.vending</item>
+ </string-array>
+
<!-- SystemUI Services: The classes of the stuff to start. -->
<string-array name="config_systemUIServiceComponents" translatable="false">
<item>com.android.systemui.util.NotificationChannels</item>
diff --git a/packages/CarSystemUI/src/com/android/systemui/sideloaded/car/CarSideLoadedAppDetector.java b/packages/CarSystemUI/src/com/android/systemui/sideloaded/car/CarSideLoadedAppDetector.java
new file mode 100644
index 000000000000..c0dbb5879d7d
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/sideloaded/car/CarSideLoadedAppDetector.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2020 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.systemui.sideloaded.car;
+
+import android.annotation.NonNull;
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.InstallSourceInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.UserHandle;
+import android.util.Log;
+
+import com.android.systemui.R;
+import com.android.systemui.car.CarDeviceProvisionedController;
+import com.android.systemui.dagger.qualifiers.Main;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * A class that detects unsafe apps.
+ * An app is considered safe if is a system app or installed through whitelisted sources.
+ */
+@Singleton
+public class CarSideLoadedAppDetector {
+ private static final String TAG = "CarSideLoadedDetector";
+
+ private final PackageManager mPackageManager;
+ private final CarDeviceProvisionedController mCarDeviceProvisionedController;
+ private final List<String> mAllowedAppInstallSources;
+
+ @Inject
+ public CarSideLoadedAppDetector(@Main Resources resources, PackageManager packageManager,
+ CarDeviceProvisionedController deviceProvisionedController) {
+ mAllowedAppInstallSources = Arrays.asList(
+ resources.getStringArray(R.array.config_allowedAppInstallSources));
+ mPackageManager = packageManager;
+ mCarDeviceProvisionedController = deviceProvisionedController;
+ }
+
+ boolean hasUnsafeInstalledApps() {
+ int userId = mCarDeviceProvisionedController.getCurrentUser();
+
+ List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+ userId);
+ for (PackageInfo info : packages) {
+ if (info.applicationInfo == null) {
+ Log.w(TAG, info.packageName + " does not have application info.");
+ return true;
+ }
+
+ if (!isSafe(info.applicationInfo)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ boolean isSafe(@NonNull ActivityManager.StackInfo stackInfo) {
+ ComponentName componentName = stackInfo.topActivity;
+ if (componentName == null) {
+ Log.w(TAG, "Stack info does not have top activity: " + stackInfo.stackId);
+ return false;
+ }
+ return isSafe(componentName.getPackageName());
+ }
+
+ private boolean isSafe(@NonNull String packageName) {
+ if (packageName == null) {
+ return false;
+ }
+
+ ApplicationInfo applicationInfo;
+ try {
+ int userId = mCarDeviceProvisionedController.getCurrentUser();
+ applicationInfo = mPackageManager.getApplicationInfoAsUser(packageName,
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+ UserHandle.of(userId));
+
+ if (applicationInfo == null) {
+ Log.e(TAG, packageName + " did not have an application info!");
+ return false;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Could not get application info for package:" + packageName, e);
+ return false;
+ }
+
+ return isSafe(applicationInfo);
+ }
+
+ private boolean isSafe(@NonNull ApplicationInfo applicationInfo) {
+ String packageName = applicationInfo.packageName;
+
+ if (applicationInfo.isSystemApp() || applicationInfo.isUpdatedSystemApp()) {
+ return true;
+ }
+
+ String initiatingPackageName;
+ try {
+ InstallSourceInfo sourceInfo = mPackageManager.getInstallSourceInfo(packageName);
+ initiatingPackageName = sourceInfo.getInitiatingPackageName();
+ if (initiatingPackageName == null) {
+ Log.w(TAG, packageName + " does not have an installer name.");
+ return false;
+ }
+
+ return mAllowedAppInstallSources.contains(initiatingPackageName);
+ } catch (IllegalArgumentException | PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ }
+}
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/sideloaded/car/CarSideLoadedAppDetectorTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/sideloaded/car/CarSideLoadedAppDetectorTest.java
new file mode 100644
index 000000000000..aebb0e005019
--- /dev/null
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/sideloaded/car/CarSideLoadedAppDetectorTest.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2020 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.systemui.sideloaded.car;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.InstallSourceInfo;
+import android.content.pm.PackageManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableResources;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.car.CarDeviceProvisionedController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class CarSideLoadedAppDetectorTest extends SysuiTestCase {
+
+ private static final String SAFE_VENDOR = "com.safe.vendor";
+ private static final String UNSAFE_VENDOR = "com.unsafe.vendor";
+ private static final String APP_PACKAGE_NAME = "com.test";
+ private static final String APP_CLASS_NAME = ".TestClass";
+
+ private CarSideLoadedAppDetector mSideLoadedAppDetector;
+
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private CarDeviceProvisionedController mCarDeviceProvisionedController;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ TestableResources testableResources = mContext.getOrCreateTestableResources();
+ String[] allowedAppInstallSources = new String[] {SAFE_VENDOR};
+ testableResources.addOverride(R.array.config_allowedAppInstallSources,
+ allowedAppInstallSources);
+
+ mSideLoadedAppDetector = new CarSideLoadedAppDetector(testableResources.getResources(),
+ mPackageManager,
+ mCarDeviceProvisionedController);
+ }
+
+ @Test
+ public void isSafe_systemApp_returnsTrue() throws Exception {
+ ActivityManager.StackInfo stackInfo = new ActivityManager.StackInfo();
+ stackInfo.topActivity = new ComponentName(APP_PACKAGE_NAME, APP_CLASS_NAME);
+
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.packageName = APP_PACKAGE_NAME;
+ applicationInfo.flags = ApplicationInfo.FLAG_SYSTEM;
+
+ when(mPackageManager.getApplicationInfoAsUser(eq(APP_PACKAGE_NAME), anyInt(), any()))
+ .thenReturn(applicationInfo);
+
+ assertThat(mSideLoadedAppDetector.isSafe(stackInfo)).isTrue();
+ }
+
+ @Test
+ public void isSafe_updatedSystemApp_returnsTrue() throws Exception {
+ ActivityManager.StackInfo stackInfo = new ActivityManager.StackInfo();
+ stackInfo.topActivity = new ComponentName(APP_PACKAGE_NAME, APP_CLASS_NAME);
+
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.packageName = APP_PACKAGE_NAME;
+ applicationInfo.flags = ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
+
+ when(mPackageManager.getApplicationInfoAsUser(eq(APP_PACKAGE_NAME), anyInt(), any()))
+ .thenReturn(applicationInfo);
+
+ assertThat(mSideLoadedAppDetector.isSafe(stackInfo)).isTrue();
+ }
+
+ @Test
+ public void isSafe_nonSystemApp_withSafeSource_returnsTrue() throws Exception {
+ InstallSourceInfo sourceInfo = new InstallSourceInfo(SAFE_VENDOR,
+ /* initiatingPackageSigningInfo= */null,
+ /* originatingPackageName= */ null,
+ /* installingPackageName= */ null);
+ ActivityManager.StackInfo stackInfo = new ActivityManager.StackInfo();
+ stackInfo.topActivity = new ComponentName(APP_PACKAGE_NAME, APP_CLASS_NAME);
+
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.packageName = APP_PACKAGE_NAME;
+
+ when(mPackageManager.getApplicationInfoAsUser(eq(APP_PACKAGE_NAME), anyInt(), any()))
+ .thenReturn(applicationInfo);
+ when(mPackageManager.getInstallSourceInfo(APP_PACKAGE_NAME)).thenReturn(sourceInfo);
+
+ assertThat(mSideLoadedAppDetector.isSafe(stackInfo)).isTrue();
+ }
+
+ @Test
+ public void isSafe_nonSystemApp_withUnsafeSource_returnsFalse() throws Exception {
+ InstallSourceInfo sourceInfo = new InstallSourceInfo(UNSAFE_VENDOR,
+ /* initiatingPackageSigningInfo= */null,
+ /* originatingPackageName= */ null,
+ /* installingPackageName= */ null);
+ ActivityManager.StackInfo stackInfo = new ActivityManager.StackInfo();
+ stackInfo.topActivity = new ComponentName(APP_PACKAGE_NAME, APP_CLASS_NAME);
+
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.packageName = APP_PACKAGE_NAME;
+
+ when(mPackageManager.getApplicationInfoAsUser(eq(APP_PACKAGE_NAME), anyInt(), any()))
+ .thenReturn(applicationInfo);
+ when(mPackageManager.getInstallSourceInfo(APP_PACKAGE_NAME)).thenReturn(sourceInfo);
+
+ assertThat(mSideLoadedAppDetector.isSafe(stackInfo)).isFalse();
+ }
+
+ @Test
+ public void isSafe_nonSystemApp_withoutSource_returnsFalse() throws Exception {
+ InstallSourceInfo sourceInfo = new InstallSourceInfo(null,
+ /* initiatingPackageSigningInfo= */null,
+ /* originatingPackageName= */ null,
+ /* installingPackageName= */ null);
+ ActivityManager.StackInfo stackInfo = new ActivityManager.StackInfo();
+ stackInfo.topActivity = new ComponentName(APP_PACKAGE_NAME, APP_CLASS_NAME);
+
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.packageName = APP_PACKAGE_NAME;
+
+ when(mPackageManager.getApplicationInfoAsUser(eq(APP_PACKAGE_NAME), anyInt(), any()))
+ .thenReturn(applicationInfo);
+ when(mPackageManager.getInstallSourceInfo(APP_PACKAGE_NAME)).thenReturn(sourceInfo);
+
+ assertThat(mSideLoadedAppDetector.isSafe(stackInfo)).isFalse();
+ }
+}