Merge "Add side loaded listener and controller" into rvc-dev am: bf3079a2ba am: 43e7ffa9b9

Change-Id: I1704eb10fcffc8b193b6184c3e97b62c1ced3e00
This commit is contained in:
Babak Bostan
2020-04-24 05:20:22 +00:00
committed by Automerger Merge Worker
8 changed files with 509 additions and 6 deletions

View File

@@ -111,5 +111,6 @@
<item>com.android.systemui.car.voicerecognition.ConnectedDeviceVoiceRecognitionNotifier</item>
<item>com.android.systemui.car.window.SystemUIOverlayWindowManager</item>
<item>com.android.systemui.car.volume.VolumeUI</item>
<item>com.android.systemui.car.sideloaded.SideLoadedAppController</item>
</string-array>
</resources>

View File

@@ -20,6 +20,7 @@ import com.android.systemui.biometrics.AuthController;
import com.android.systemui.bubbles.dagger.BubbleModule;
import com.android.systemui.car.navigationbar.CarNavigationBar;
import com.android.systemui.car.notification.CarNotificationModule;
import com.android.systemui.car.sideloaded.SideLoadedAppController;
import com.android.systemui.car.statusbar.CarStatusBar;
import com.android.systemui.car.statusbar.CarStatusBarModule;
import com.android.systemui.car.voicerecognition.ConnectedDeviceVoiceRecognitionNotifier;
@@ -192,4 +193,10 @@ public abstract class CarSystemUIBinder {
@IntoMap
@ClassKey(SystemUIOverlayWindowManager.class)
public abstract SystemUI bindSystemUIPrimaryWindowManager(SystemUIOverlayWindowManager sysui);
/** Inject into SideLoadedAppController. */
@Binds
@IntoMap
@ClassKey(SideLoadedAppController.class)
public abstract SystemUI bindSideLoadedAppController(SideLoadedAppController sysui);
}

View File

@@ -0,0 +1,73 @@
/*
* 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.car.sideloaded;
import android.app.IActivityTaskManager;
import android.content.Context;
import android.os.RemoteException;
import android.util.Log;
import com.android.systemui.SystemUI;
import javax.inject.Inject;
import javax.inject.Singleton;
/**
* Controller responsible for detecting unsafe apps.
*/
@Singleton
public class SideLoadedAppController extends SystemUI {
private static final String TAG = SideLoadedAppController.class.getSimpleName();
private IActivityTaskManager mActivityTaskManager;
private SideLoadedAppListener mSideLoadedAppListener;
private SideLoadedAppDetector mSideLoadedAppDetector;
private SideLoadedAppStateController mSideLoadedAppStateController;
@Inject
public SideLoadedAppController(Context context,
IActivityTaskManager activityTaskManager,
SideLoadedAppDetector sideLoadedAppDetector,
SideLoadedAppListener sideLoadedAppListener,
SideLoadedAppStateController sideLoadedAppStateController) {
super(context);
mSideLoadedAppDetector = sideLoadedAppDetector;
mActivityTaskManager = activityTaskManager;
mSideLoadedAppListener = sideLoadedAppListener;
mSideLoadedAppStateController = sideLoadedAppStateController;
}
@Override
public void start() {
}
@Override
protected void onBootCompleted() {
Log.i(TAG, "OnBootCompleted");
try {
mActivityTaskManager.registerTaskStackListener(mSideLoadedAppListener);
} catch (RemoteException e) {
Log.e(TAG, "Could not register car side loaded app listener.", e);
}
if (mSideLoadedAppDetector.hasUnsafeInstalledApps()) {
mSideLoadedAppStateController.onUnsafeInstalledAppsDetected();
}
}
}

View File

@@ -42,15 +42,15 @@ import javax.inject.Singleton;
* 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";
public class SideLoadedAppDetector {
private static final String TAG = SideLoadedAppDetector.class.getSimpleName();
private final PackageManager mPackageManager;
private final CarDeviceProvisionedController mCarDeviceProvisionedController;
private final List<String> mAllowedAppInstallSources;
@Inject
public CarSideLoadedAppDetector(@Main Resources resources, PackageManager packageManager,
public SideLoadedAppDetector(@Main Resources resources, PackageManager packageManager,
CarDeviceProvisionedController deviceProvisionedController) {
mAllowedAppInstallSources = Arrays.asList(
resources.getStringArray(R.array.config_allowedAppInstallSources));

View File

@@ -0,0 +1,130 @@
/*
* 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.car.sideloaded;
import android.app.ActivityManager;
import android.app.ActivityManager.StackInfo;
import android.app.IActivityTaskManager;
import android.app.TaskStackListener;
import android.content.ComponentName;
import android.hardware.display.DisplayManager;
import android.os.RemoteException;
import android.util.Log;
import android.view.Display;
import java.util.List;
import javax.inject.Inject;
/**
* A TaskStackListener to detect when an unsafe app is launched/foregrounded.
*/
public class SideLoadedAppListener extends TaskStackListener {
private static final String TAG = SideLoadedAppListener.class.getSimpleName();
private IActivityTaskManager mActivityTaskManager;
private DisplayManager mDisplayManager;
private SideLoadedAppDetector mSideLoadedAppDetector;
private SideLoadedAppStateController mSideLoadedAppStateController;
@Inject
SideLoadedAppListener(SideLoadedAppDetector sideLoadedAppDetector,
IActivityTaskManager activityTaskManager,
DisplayManager displayManager,
SideLoadedAppStateController sideLoadedAppStateController) {
mSideLoadedAppDetector = sideLoadedAppDetector;
mActivityTaskManager = activityTaskManager;
mDisplayManager = displayManager;
mSideLoadedAppStateController = sideLoadedAppStateController;
}
@Override
public void onTaskCreated(int taskId, ComponentName componentName) throws RemoteException {
super.onTaskCreated(taskId, componentName);
List<StackInfo> stackInfoList = mActivityTaskManager.getAllStackInfos();
ActivityManager.StackInfo stackInfo = getStackInfo(stackInfoList, taskId);
if (stackInfo == null) {
Log.e(TAG, "Stack info was not available for taskId: " + taskId);
return;
}
if (!mSideLoadedAppDetector.isSafe(stackInfo)) {
Display display = mDisplayManager.getDisplay(stackInfo.displayId);
mSideLoadedAppStateController.onUnsafeTaskCreatedOnDisplay(display);
}
}
@Override
public void onTaskStackChanged() throws RemoteException {
super.onTaskStackChanged();
Display[] displays = mDisplayManager.getDisplays();
for (Display display : displays) {
// Note that the stackInfoList is ordered by recency.
List<StackInfo> stackInfoList =
mActivityTaskManager.getAllStackInfosOnDisplay(display.getDisplayId());
if (stackInfoList == null) {
continue;
}
StackInfo stackInfo = getTopVisibleStackInfo(stackInfoList);
if (stackInfo == null) {
continue;
}
if (mSideLoadedAppDetector.isSafe(stackInfo)) {
mSideLoadedAppStateController.onSafeTaskDisplayedOnDisplay(display);
} else {
mSideLoadedAppStateController.onUnsafeTaskDisplayedOnDisplay(display);
}
}
}
/**
* Returns stack info for a given taskId.
*/
private ActivityManager.StackInfo getStackInfo(
List<ActivityManager.StackInfo> stackInfoList, int taskId) {
if (stackInfoList == null) {
return null;
}
for (ActivityManager.StackInfo stackInfo : stackInfoList) {
if (stackInfo.taskIds == null) {
continue;
}
for (int stackTaskId : stackInfo.taskIds) {
if (taskId == stackTaskId) {
return stackInfo;
}
}
}
return null;
}
/**
* Returns the first visible stackInfo.
*/
private ActivityManager.StackInfo getTopVisibleStackInfo(
List<ActivityManager.StackInfo> stackInfoList) {
for (ActivityManager.StackInfo stackInfo : stackInfoList) {
if (stackInfo.visible) {
return stackInfo;
}
}
return null;
}
}

View File

@@ -0,0 +1,51 @@
/*
* 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.car.sideloaded;
import android.util.Log;
import android.view.Display;
import javax.inject.Inject;
import javax.inject.Singleton;
/**
* Manager responsible for displaying proper UI when an unsafe app is detected.
*/
@Singleton
public class SideLoadedAppStateController {
private static final String TAG = SideLoadedAppStateController.class.getSimpleName();
@Inject
SideLoadedAppStateController() {
}
void onUnsafeInstalledAppsDetected() {
Log.d(TAG, "Unsafe installed apps detected.");
}
void onUnsafeTaskCreatedOnDisplay(Display display) {
Log.d(TAG, "Unsafe task created on display " + display.getDisplayId() + ".");
}
void onSafeTaskDisplayedOnDisplay(Display display) {
Log.d(TAG, "Safe task displayed on display " + display.getDisplayId() + ".");
}
void onUnsafeTaskDisplayedOnDisplay(Display display) {
Log.d(TAG, "Unsafe task displayed on display " + display.getDisplayId() + ".");
}
}

View File

@@ -47,14 +47,14 @@ import org.mockito.MockitoAnnotations;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
public class CarSideLoadedAppDetectorTest extends SysuiTestCase {
public class SideLoadedAppDetectorTest 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;
private SideLoadedAppDetector mSideLoadedAppDetector;
@Mock
private PackageManager mPackageManager;
@@ -70,7 +70,7 @@ public class CarSideLoadedAppDetectorTest extends SysuiTestCase {
testableResources.addOverride(R.array.config_allowedAppInstallSources,
allowedAppInstallSources);
mSideLoadedAppDetector = new CarSideLoadedAppDetector(testableResources.getResources(),
mSideLoadedAppDetector = new SideLoadedAppDetector(testableResources.getResources(),
mPackageManager,
mCarDeviceProvisionedController);
}

View File

@@ -0,0 +1,241 @@
/*
* 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.car.sideloaded;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.ActivityManager.StackInfo;
import android.app.IActivityTaskManager;
import android.content.ComponentName;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerGlobal;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.Display;
import android.view.DisplayAdjustments;
import android.view.DisplayInfo;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
public class SideLoadedAppListenerTest extends SysuiTestCase {
private static final String APP_PACKAGE_NAME = "com.test";
private static final String APP_CLASS_NAME = ".TestClass";
private SideLoadedAppListener mSideLoadedAppListener;
@Mock
private SideLoadedAppDetector mSideLoadedAppDetector;
@Mock
private DisplayManager mDisplayManager;
@Mock
private IActivityTaskManager mActivityTaskManager;
@Mock
private SideLoadedAppStateController mSideLoadedAppStateController;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mSideLoadedAppListener = new SideLoadedAppListener(mSideLoadedAppDetector,
mActivityTaskManager, mDisplayManager, mSideLoadedAppStateController);
}
@Test
public void onTaskCreated_safeTask_callsNoMethods() throws Exception {
int taskId = 999;
int displayId = 123;
ComponentName componentName = new ComponentName(APP_PACKAGE_NAME, APP_CLASS_NAME);
StackInfo stackInfo1 = createTask(1, /* isVisible= */ true);
stackInfo1.taskIds = new int[] { 11, 22, 33 };
StackInfo stackInfo2 = createTask(2, /* isVisible= */ true);
stackInfo2.taskIds = new int[] { 111, 222, 333, taskId };
stackInfo2.displayId = displayId;
List<StackInfo> stackInfoList = Arrays.asList(stackInfo1, stackInfo2);
when(mActivityTaskManager.getAllStackInfos()).thenReturn(stackInfoList);
when(mSideLoadedAppDetector.isSafe(stackInfo2)).thenReturn(true);
mSideLoadedAppListener.onTaskCreated(taskId, componentName);
verify(mSideLoadedAppDetector, never()).isSafe(stackInfo1);
verify(mSideLoadedAppDetector).isSafe(stackInfo2);
verify(mSideLoadedAppStateController, never()).onUnsafeTaskCreatedOnDisplay(any());
verify(mSideLoadedAppStateController, never()).onSafeTaskDisplayedOnDisplay(any());
verify(mSideLoadedAppStateController, never()).onUnsafeTaskDisplayedOnDisplay(any());
}
@Test
public void onTaskCreated_unsafeTask_callsUnsafeTaskCreated() throws Exception {
int taskId = 999;
int displayId = 123;
ComponentName componentName = new ComponentName(APP_PACKAGE_NAME, APP_CLASS_NAME);
StackInfo stackInfo1 = createTask(1, /* isVisible= */ true);
stackInfo1.taskIds = new int[] { 11, 22, 33 };
StackInfo stackInfo2 = createTask(2, /* isVisible= */ true);
stackInfo2.taskIds = new int[] { 111, 222, 333, taskId };
stackInfo2.displayId = displayId;
List<StackInfo> stackInfoList = Arrays.asList(stackInfo1, stackInfo2);
Display display = createDisplay(displayId);
when(mActivityTaskManager.getAllStackInfos()).thenReturn(stackInfoList);
when(mSideLoadedAppDetector.isSafe(stackInfo2)).thenReturn(false);
when(mDisplayManager.getDisplay(displayId)).thenReturn(display);
mSideLoadedAppListener.onTaskCreated(taskId, componentName);
verify(mSideLoadedAppDetector, never()).isSafe(stackInfo1);
verify(mSideLoadedAppDetector).isSafe(stackInfo2);
verify(mSideLoadedAppStateController).onUnsafeTaskCreatedOnDisplay(display);
verify(mSideLoadedAppStateController, never()).onSafeTaskDisplayedOnDisplay(any());
verify(mSideLoadedAppStateController, never()).onUnsafeTaskDisplayedOnDisplay(any());
}
@Test
public void onTaskStackChanged_safeTask_callsSafeTaskDisplayed() throws Exception {
Display display = createDisplay(123);
StackInfo stackInfo1 = createTask(1, /* isVisible= */ false);
StackInfo stackInfo2 = createTask(2, /* isVisible= */ true);
StackInfo stackInfo3 = createTask(3, /* isVisible= */ true);
List<StackInfo> stackInfoList = Arrays.asList(stackInfo1, stackInfo2, stackInfo3);
when(mActivityTaskManager.getAllStackInfosOnDisplay(display.getDisplayId()))
.thenReturn(stackInfoList);
when(mSideLoadedAppDetector.isSafe(stackInfo2)).thenReturn(true);
when(mDisplayManager.getDisplays()).thenReturn(new Display[] { display });
mSideLoadedAppListener.onTaskStackChanged();
verify(mSideLoadedAppDetector, never()).isSafe(stackInfo1);
verify(mSideLoadedAppDetector).isSafe(stackInfo2);
verify(mSideLoadedAppDetector, never()).isSafe(stackInfo3);
verify(mSideLoadedAppStateController, never()).onUnsafeTaskCreatedOnDisplay(any());
verify(mSideLoadedAppStateController).onSafeTaskDisplayedOnDisplay(display);
verify(mSideLoadedAppStateController, never()).onUnsafeTaskDisplayedOnDisplay(any());
}
@Test
public void onTaskStackChanged_unsafeTask_callsUnsafeTaskDisplayed() throws Exception {
Display display = createDisplay(123);
StackInfo stackInfo1 = createTask(1, /* isVisible= */ false);
StackInfo stackInfo2 = createTask(2, /* isVisible= */ true);
StackInfo stackInfo3 = createTask(3, /* isVisible= */ true);
List<StackInfo> stackInfoList = Arrays.asList(stackInfo1, stackInfo2, stackInfo3);
when(mActivityTaskManager.getAllStackInfosOnDisplay(display.getDisplayId()))
.thenReturn(stackInfoList);
when(mSideLoadedAppDetector.isSafe(stackInfo2)).thenReturn(false);
when(mDisplayManager.getDisplays()).thenReturn(new Display[] { display });
mSideLoadedAppListener.onTaskStackChanged();
verify(mSideLoadedAppDetector, never()).isSafe(stackInfo1);
verify(mSideLoadedAppDetector).isSafe(stackInfo2);
verify(mSideLoadedAppDetector, never()).isSafe(stackInfo3);
verify(mSideLoadedAppStateController, never()).onUnsafeTaskCreatedOnDisplay(any());
verify(mSideLoadedAppStateController, never()).onSafeTaskDisplayedOnDisplay(any());
verify(mSideLoadedAppStateController).onUnsafeTaskDisplayedOnDisplay(display);
}
@Test
public void onTaskStackChanged_multiDisplay_callsTasksDisplayed() throws Exception {
Display display1 = createDisplay(1);
StackInfo stackInfo1 = createTask(1, /* isVisible= */ false);
StackInfo stackInfo2 = createTask(2, /* isVisible= */ true);
StackInfo stackInfo3 = createTask(3, /* isVisible= */ true);
List<StackInfo> display1Stack = Arrays.asList(stackInfo1, stackInfo2, stackInfo3);
Display display2 = createDisplay(2);
StackInfo stackInfo4 = createTask(4, /* isVisible= */ true);
List<StackInfo> display2Stack = Collections.singletonList(stackInfo4);
Display display3 = createDisplay(3);
StackInfo stackInfo5 = createTask(5, /* isVisible= */ true);
List<StackInfo> display3Stack = Collections.singletonList(stackInfo5);
when(mActivityTaskManager.getAllStackInfosOnDisplay(display1.getDisplayId()))
.thenReturn(display1Stack);
when(mActivityTaskManager.getAllStackInfosOnDisplay(display2.getDisplayId()))
.thenReturn(display2Stack);
when(mActivityTaskManager.getAllStackInfosOnDisplay(display3.getDisplayId()))
.thenReturn(display3Stack);
when(mSideLoadedAppDetector.isSafe(stackInfo2)).thenReturn(true);
when(mSideLoadedAppDetector.isSafe(stackInfo4)).thenReturn(false);
when(mSideLoadedAppDetector.isSafe(stackInfo5)).thenReturn(true);
when(mDisplayManager.getDisplays())
.thenReturn(new Display[] { display1, display2, display3});
mSideLoadedAppListener.onTaskStackChanged();
verify(mSideLoadedAppDetector, never()).isSafe(stackInfo1);
verify(mSideLoadedAppDetector).isSafe(stackInfo2);
verify(mSideLoadedAppDetector, never()).isSafe(stackInfo3);
verify(mSideLoadedAppDetector).isSafe(stackInfo4);
verify(mSideLoadedAppDetector).isSafe(stackInfo5);
verify(mSideLoadedAppStateController, never()).onUnsafeTaskCreatedOnDisplay(any());
verify(mSideLoadedAppStateController).onSafeTaskDisplayedOnDisplay(display1);
verify(mSideLoadedAppStateController, never()).onUnsafeTaskDisplayedOnDisplay(display2);
verify(mSideLoadedAppStateController).onSafeTaskDisplayedOnDisplay(display3);
verify(mSideLoadedAppStateController, never()).onUnsafeTaskDisplayedOnDisplay(display1);
verify(mSideLoadedAppStateController).onUnsafeTaskDisplayedOnDisplay(display2);
verify(mSideLoadedAppStateController, never()).onUnsafeTaskDisplayedOnDisplay(display3);
}
private Display createDisplay(int id) {
return new Display(DisplayManagerGlobal.getInstance(),
id,
new DisplayInfo(),
DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
}
private StackInfo createTask(int id, boolean isVisible) {
StackInfo stackInfo = new StackInfo();
stackInfo.stackId = id;
stackInfo.visible = isVisible;
return stackInfo;
}
}