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

Commit 1fbf0eb1 authored by Hall Liu's avatar Hall Liu Committed by Automerger Merge Worker
Browse files

Fix a car mode tracking leak upon uninstall am: 68518e59 am: a63a9302 am: f8096c68

Original change: https://android-review.googlesource.com/c/platform/packages/services/Telecomm/+/1393876

Change-Id: If05c68cda1cc737cf32cce267c968880f1e7e1e1
parents 6228277e f8096c68
Loading
Loading
Loading
Loading
+23 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import com.android.internal.util.IndentingPrintWriter;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.PriorityQueue;
import java.util.stream.Collectors;

@@ -143,6 +144,28 @@ public class CarModeTracker {
        mCarModeApps.removeIf(c -> c.getPriority() == priority);
    }

    /**
     * Force-removes a package from the car mode tracking list, no matter at which priority.
     *
     * This handles the case where packages are disabled or uninstalled. In those case, remove them
     * from the tracking list so they don't cause a leak.
     * @param packageName Package name of the app to force-remove
     */
    public void forceExitCarMode(@NonNull String packageName) {
        Optional<CarModeApp> forcedApp = mCarModeApps.stream()
                .filter(c -> c.getPackageName().equals(packageName))
                .findAny();
        if (forcedApp.isPresent()) {
            String logString = String.format("forceExitCarMode: packageName=%s, was at priority=%s",
                    packageName, forcedApp.get().getPriority());
            Log.i(this, logString);
            mCarModeChangeLog.log(logString);
            mCarModeApps.removeIf(c -> c.getPackageName().equals(packageName));
        } else {
            Log.i(this, "Package %s is not tracked as requesting car mode", packageName);
        }
    }

    /**
     * Retrieves a list of the apps which are currently in car mode, ordered by priority such that
     * the highest priority app is first.
+19 −5
Original line number Diff line number Diff line
@@ -900,9 +900,18 @@ public class InCallController extends CallsManagerListenerBase {
        }
    };

    private final SystemStateListener mSystemStateListener =
            (priority, packageName, isCarMode) -> InCallController.this.handleCarModeChange(
                    priority, packageName, isCarMode);
    private final SystemStateListener mSystemStateListener = new SystemStateListener() {
        @Override
        public void onCarModeChanged(int priority, String packageName, boolean isCarMode) {
            InCallController.this.handleCarModeChange(priority, packageName, isCarMode);
        }

        @Override
        public void onPackageUninstalled(String packageName) {
            mCarModeTracker.forceExitCarMode(packageName);
            updateCarModeForSwitchingConnection();
        }
    };

    private static final int IN_CALL_SERVICE_TYPE_INVALID = 0;
    private static final int IN_CALL_SERVICE_TYPE_DIALER_UI = 1;
@@ -1860,7 +1869,8 @@ public class InCallController extends CallsManagerListenerBase {
    public void handleCarModeChange(int priority, String packageName, boolean isCarMode) {
        Log.i(this, "handleCarModeChange: packageName=%s, priority=%d, isCarMode=%b",
                packageName, priority, isCarMode);
        if (!isCarModeInCallService(packageName)) {
        // Don't ignore the signal if we are disabling car mode; package may be uninstalled.
        if (isCarMode && !isCarModeInCallService(packageName)) {
            Log.i(this, "handleCarModeChange: not a valid InCallService; packageName=%s",
                    packageName);
            return;
@@ -1872,8 +1882,12 @@ public class InCallController extends CallsManagerListenerBase {
            mCarModeTracker.handleExitCarMode(priority, packageName);
        }

        updateCarModeForSwitchingConnection();
    }

    public void updateCarModeForSwitchingConnection() {
        if (mInCallServiceConnection != null) {
            Log.i(this, "handleCarModeChange: car mode apps: %s",
            Log.i(this, "updateCarModeForSwitchingConnection: car mode apps: %s",
                    mCarModeTracker.getCarModeApps().stream().collect(Collectors.joining(", ")));
            if (shouldUseCarModeUI()) {
                mInCallServiceConnection.changeCarModeApp(
+30 −8
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.net.Uri;
import android.telecom.Log;

import java.util.Set;
@@ -38,7 +39,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
 * Provides various system states to the rest of the telecom codebase.
 */
public class SystemStateHelper {
    public static interface SystemStateListener {
    public interface SystemStateListener {
        /**
         * Listener method to inform interested parties when a package name requests to enter or
         * exit car mode.
@@ -48,6 +49,12 @@ public class SystemStateHelper {
         *                              otherwise.
         */
        void onCarModeChanged(int priority, String packageName, boolean isCarMode);

        /**
         * Notifies when a package has been uninstalled.
         * @param packageName the package name of the uninstalled package
         */
        void onPackageUninstalled(String packageName);
    }

    private final Context mContext;
@@ -62,7 +69,7 @@ public class SystemStateHelper {
                            UiModeManager.DEFAULT_PRIORITY);
                    String callingPackage = intent.getStringExtra(
                            UiModeManager.EXTRA_CALLING_PACKAGE);
                    Log.i(SystemStateHelper.this, "ENTER_CAR_MODE_PRIVILEGED; priority=%d, pkg=%s",
                    Log.i(SystemStateHelper.this, "ENTER_CAR_MODE_PRIORITIZED; priority=%d, pkg=%s",
                            priority, callingPackage);
                    onEnterCarMode(priority, callingPackage);
                } else if (UiModeManager.ACTION_EXIT_CAR_MODE_PRIORITIZED.equals(action)) {
@@ -70,11 +77,21 @@ public class SystemStateHelper {
                            UiModeManager.DEFAULT_PRIORITY);
                    String callingPackage = intent.getStringExtra(
                            UiModeManager.EXTRA_CALLING_PACKAGE);
                    Log.i(SystemStateHelper.this, "EXIT_CAR_MODE_PRIVILEGED; priority=%d, pkg=%s",
                    Log.i(SystemStateHelper.this, "EXIT_CAR_MODE_PRIORITIZED; priority=%d, pkg=%s",
                            priority, callingPackage);
                    onExitCarMode(priority, callingPackage);
                } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
                    Uri data = intent.getData();
                    if (data == null) {
                        Log.w(SystemStateHelper.this,
                                "Got null data for package removed, ignoring");
                        return;
                    }
                    mListeners.forEach(
                            l -> l.onPackageUninstalled(data.getEncodedSchemeSpecificPart()));
                } else {
                    Log.w(this, "Unexpected intent received: %s", intent.getAction());
                    Log.w(SystemStateHelper.this,
                            "Unexpected intent received: %s", intent.getAction());
                }
            } finally {
                Log.endSession();
@@ -88,11 +105,16 @@ public class SystemStateHelper {
    public SystemStateHelper(Context context) {
        mContext = context;

        IntentFilter intentFilter = new IntentFilter(
        IntentFilter intentFilter1 = new IntentFilter(
                UiModeManager.ACTION_ENTER_CAR_MODE_PRIORITIZED);
        intentFilter.addAction(UiModeManager.ACTION_EXIT_CAR_MODE_PRIORITIZED);
        mContext.registerReceiver(mBroadcastReceiver, intentFilter);
        Log.i(this, "Registering car mode receiver: %s", intentFilter);
        intentFilter1.addAction(UiModeManager.ACTION_EXIT_CAR_MODE_PRIORITIZED);

        IntentFilter intentFilter2 = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
        intentFilter2.addDataScheme("package");
        mContext.registerReceiver(mBroadcastReceiver, intentFilter1);
        mContext.registerReceiver(mBroadcastReceiver, intentFilter2);
        Log.i(this, "Registering broadcast receiver: %s", intentFilter1);
        Log.i(this, "Registering broadcast receiver: %s", intentFilter2);

        mIsCarMode = getSystemCarMode();
    }
+12 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.telecom.tests;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static junit.framework.TestCase.assertNull;

@@ -103,6 +104,17 @@ public class CarModeTrackerTest extends TelecomTestCase {
        assertEquals(CAR_MODE_APP1_PACKAGE_NAME, mCarModeTracker.getCarModeApps().get(0));
    }

    /**
     * Ensure that we don't keep a package around after it's been removed from the device
     */
    @Test
    public void testForceExitCarMode() {
        testEnterCarModeBasic();
        mCarModeTracker.forceExitCarMode(CAR_MODE_APP1_PACKAGE_NAME);
        assertFalse(mCarModeTracker.isInCarMode());
        assertNull(mCarModeTracker.getCurrentCarModePackage());
    }

    /**
     * Verifies only the first app at the default priority gets tracked.
     */
+29 −1
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -66,6 +67,7 @@ import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.test.mock.MockContext;
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
import android.text.TextUtils;

import com.android.internal.telecom.IInCallAdapter;
@@ -146,6 +148,8 @@ public class InCallControllerTests extends TelecomTestCase {
    private InCallController mInCallController;
    private TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() {};
    private EmergencyCallHelper mEmergencyCallHelper;
    private SystemStateHelper.SystemStateListener mSystemStateListener;
    private CarModeTracker mCarModeTracker = spy(new CarModeTracker());

    @Override
    @Before
@@ -166,7 +170,13 @@ public class InCallControllerTests extends TelecomTestCase {
        when(mMockCallsManager.getRoleManagerAdapter()).thenReturn(mMockRoleManagerAdapter);
        mInCallController = new InCallController(mMockContext, mLock, mMockCallsManager,
                mMockSystemStateHelper, mDefaultDialerCache, mTimeoutsAdapter,
                mEmergencyCallHelper, new CarModeTracker(), mClockProxy);
                mEmergencyCallHelper, mCarModeTracker, mClockProxy);

        ArgumentCaptor<SystemStateHelper.SystemStateListener> systemStateListenerArgumentCaptor
                = ArgumentCaptor.forClass(SystemStateHelper.SystemStateListener.class);
        verify(mMockSystemStateHelper).addListener(systemStateListenerArgumentCaptor.capture());
        mSystemStateListener = systemStateListenerArgumentCaptor.getValue();

        when(mMockContext.getSystemService(eq(Context.NOTIFICATION_SERVICE)))
                .thenReturn(mNotificationManager);
        // Companion Apps don't have CONTROL_INCALL_EXPERIENCE permission.
@@ -210,6 +220,24 @@ public class InCallControllerTests extends TelecomTestCase {
        super.tearDown();
    }

    @SmallTest
    @Test
    public void testCarModeAppRemoval() {
        setupMockPackageManager(true /* default */, true /* system */, true /* external calls */);
        when(mMockCallsManager.getCurrentUserHandle()).thenReturn(mUserHandle);
        when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);

        when(mMockSystemStateHelper.isCarMode()).thenReturn(true);

        mSystemStateListener.onCarModeChanged(666, CAR_PKG, true);
        verify(mCarModeTracker).handleEnterCarMode(666, CAR_PKG);
        assertTrue(mCarModeTracker.isInCarMode());

        mSystemStateListener.onPackageUninstalled(CAR_PKG);
        verify(mCarModeTracker).forceExitCarMode(CAR_PKG);
        assertFalse(mCarModeTracker.isInCarMode());
    }

    @MediumTest
    @Test
    public void testBindToService_NoServicesFound_IncomingCall() throws Exception {
Loading