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

Commit ed0624e6 authored by Hyangseok Chae's avatar Hyangseok Chae Committed by chaviw
Browse files

Ignore PIP mode request for destroyed activity

PIP request can be postponed if keyguard is on.
If the activity is destroyed before keyguard is dismissed,
Then PIP operation causes NullPointerException.

In such case we should ignore PIP mode request.

Test:
Manual test -
 1.Configure Keyguard (Pattern, Password) for secure lock
 2.Receive a WhatsApp video call
 3.Press Back Key (WhatsApp enters into PIP mode)
 4.Caller ends a video call
 5.When user (receiver) unlocks the screen, system reboots
Test: atest WmTests:ActivityTaskManagerServiceTests#testEnterPipModeWhenRecordParentChangesToNull

Bug: 144045134

Change-Id: I8f67e57b2bfaa187829622b050c6ce387d749e8d
(cherry-picked from ae2c5e52efbd59b6bccd5b677b4047a292fe86fc)
parent df155220
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -4176,6 +4176,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {

                final Runnable enterPipRunnable = () -> {
                    synchronized (mGlobalLock) {
                        if (r.getParent() == null) {
                            Slog.e(TAG, "Skip enterPictureInPictureMode, destroyed " + r);
                            return;
                        }
                        // Only update the saved args from the args that are set
                        r.pictureInPictureArgs.copyOnlySet(params);
                        final float aspectRatio = r.pictureInPictureArgs.getAspectRatio();
+45 −0
Original line number Diff line number Diff line
@@ -19,15 +19,21 @@ package com.android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;

import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;

import android.app.Activity;
import android.app.ActivityManager;
import android.app.PictureInPictureParams;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.IBinder;
import android.view.IDisplayWindowListener;
import android.view.WindowContainerTransaction;

@@ -36,6 +42,7 @@ import androidx.test.filters.MediumTest;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoSession;

import java.util.ArrayList;

@@ -148,5 +155,43 @@ public class ActivityTaskManagerServiceTests extends ActivityTestsBase {
        assertEquals(0, changed.size());
        assertEquals(1, removed.size());
    }

    /*
        a test to verify b/144045134 - ignore PIP mode request for destroyed activity.
        mocks r.getParent() to return null to cause NPE inside enterPipRunnable#run() in
        ActivityTaskMangerservice#enterPictureInPictureMode(), which rebooted the device.
        It doesn't fully simulate the issue's reproduce steps, but this should suffice.
     */
    @Test
    public void testEnterPipModeWhenRecordParentChangesToNull() {
        MockitoSession mockSession = mockitoSession()
                .initMocks(this)
                .mockStatic(ActivityRecord.class)
                .startMocking();

        ActivityRecord record = mock(ActivityRecord.class);
        IBinder token = mock(IBinder.class);
        PictureInPictureParams params = mock(PictureInPictureParams.class);
        record.pictureInPictureArgs = params;

        //mock operations in private method ensureValidPictureInPictureActivityParamsLocked()
        when(ActivityRecord.forTokenLocked(token)).thenReturn(record);
        doReturn(true).when(record).supportsPictureInPicture();
        doReturn(false).when(params).hasSetAspectRatio();

        //mock other operations
        doReturn(true).when(record)
                .checkEnterPictureInPictureState("enterPictureInPictureMode", false);
        doReturn(false).when(mService).isInPictureInPictureMode(any());
        doReturn(false).when(mService).isKeyguardLocked();

        //to simulate NPE
        doReturn(null).when(record).getParent();

        mService.enterPictureInPictureMode(token, params);
        //if record's null parent is not handled gracefully, test will fail with NPE

        mockSession.finishMocking();
    }
}