Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 16648aff authored by Babak's avatar Babak
Browse files

Add side loaded listener and controller

- Add SideLoadedAppController as a SysUI class to kick off the
sideloaded app tracking.
- Add SideLoadedAppStateController to manage the state of sideloaded
apps. This class is just a wireframe and will be filled in the following
cls.
- Add SideLoadedAppListener to listen for stack changes and notify the
state controller when the sideloaded apps state changes.
- Add unit test for SideLoadedAppListener

Bug: 154263570
Test: atest (All tests in CarSystemUITests)

Change-Id: I8d2b9b3d224017d9a51e2590a1aae0a9a265f7c3
parent 07bed80a
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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>
+7 −0
Original line number Diff line number Diff line
@@ -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);
}
+73 −0
Original line number Diff line number Diff line
/*
 * 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();
        }
    }
}
+3 −3
Original line number Diff line number Diff line
@@ -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));
+130 −0
Original line number Diff line number Diff line
/*
 * 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;
    }
}
Loading