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

Commit 91e5d7fa authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Remove locked user tasks if starting overlay fails"

parents 42050571 1d996bd7
Loading
Loading
Loading
Loading
+47 −4
Original line number Diff line number Diff line
@@ -17,27 +17,37 @@
package com.android.systemui.keyguard;

import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.IActivityManager;
import android.app.KeyguardManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;

import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener;

public class WorkLockActivityController {
    private final Context mContext;
    final SystemServicesProxy mSsp;
    private final SystemServicesProxy mSsp;
    private final IActivityManager mIam;

    public WorkLockActivityController(Context context) {
        this(context, SystemServicesProxy.getInstance(context), ActivityManager.getService());
    }

    @VisibleForTesting
    WorkLockActivityController(Context context, SystemServicesProxy ssp, IActivityManager am) {
        mContext = context;
        mSsp = SystemServicesProxy.getInstance(context);
        mSsp = ssp;
        mIam = am;

        EventBus.getDefault().register(this);
        mSsp.registerTaskStackListener(mLockListener);
    }

@@ -52,7 +62,40 @@ public class WorkLockActivityController {
        final ActivityOptions options = ActivityOptions.makeBasic();
        options.setLaunchTaskId(taskId);
        options.setTaskOverlay(true, false /* canResume */);
        mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT);

        final int result = startActivityAsUser(intent, options.toBundle(), UserHandle.USER_CURRENT);
        if (result >= ActivityManager.START_SUCCESS) {
            // OK
        } else {
            // Starting the activity inside the task failed. We can't be sure why, so to be
            // safe just remove the whole task if it still exists.
            mSsp.removeTask(taskId);
        }
    }

    /**
     * Version of {@link Context#startActivityAsUser} which keeps the success code from
     * IActivityManager, so we can read back whether ActivityManager thinks it started properly.
     */
    private int startActivityAsUser(Intent intent, Bundle options, int userId) {
        try {
            return mIam.startActivityAsUser(
                    mContext.getIApplicationThread() /*caller*/,
                    mContext.getBasePackageName() /*callingPackage*/,
                    intent /*intent*/,
                    intent.resolveTypeIfNeeded(mContext.getContentResolver()) /*resolvedType*/,
                    null /*resultTo*/,
                    null /*resultWho*/,
                    0 /*requestCode*/,
                    Intent.FLAG_ACTIVITY_NEW_TASK /*flags*/,
                    null /*profilerInfo*/,
                    options /*options*/,
                    userId /*user*/);
        } catch (RemoteException e) {
            return ActivityManager.START_CANCELED;
        } catch (Exception e) {
            return ActivityManager.START_CANCELED;
        }
    }

    private final TaskStackListener mLockListener = new TaskStackListener() {
+169 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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.keyguard;

import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.argThat;

import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.IActivityManager;
import android.app.IApplicationThread;
import android.app.ProfilerInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
import android.os.UserHandle;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;

import com.android.systemui.keyguard.WorkLockActivity;
import com.android.systemui.keyguard.WorkLockActivityController;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

/**
 * runtest systemui -c com.android.systemui.keyguard.WorkLockActivityControllerTest
 */
@SmallTest
@RunWith(AndroidJUnit4.class)
public class WorkLockActivityControllerTest {
    private static final int USER_ID = 333;
    private static final int TASK_ID = 444;

    private @Mock Context mContext;
    private @Mock SystemServicesProxy mSystemServicesProxy;
    private @Mock IActivityManager mIActivityManager;

    private WorkLockActivityController mController;
    private TaskStackListener mTaskStackListener;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);

        // Set a package name to use for checking ComponentName well-formedness in tests.
        doReturn("com.example.test").when(mContext).getPackageName();

        // Construct controller. Save the TaskStackListener for injecting events.
        final ArgumentCaptor<TaskStackListener> listenerCaptor =
                ArgumentCaptor.forClass(TaskStackListener.class);
        mController =
                new WorkLockActivityController(mContext, mSystemServicesProxy, mIActivityManager);

        verify(mSystemServicesProxy).registerTaskStackListener(listenerCaptor.capture());
        mTaskStackListener = listenerCaptor.getValue();
    }

    @Test
    public void testOverlayStartedWhenLocked() throws Exception {
        // When starting an activity succeeds,
        setActivityStartCode(TASK_ID, true /*taskOverlay*/, ActivityManager.START_SUCCESS);

        // And the controller receives a message saying the profile is locked,
        mTaskStackListener.onTaskProfileLocked(TASK_ID, USER_ID);

        // The overlay should start and the task the activity started in should not be removed.
        verifyStartActivity(TASK_ID, true /*taskOverlay*/);
        verify(mSystemServicesProxy, never()).removeTask(anyInt() /*taskId*/);
    }

    @Test
    public void testRemoveTaskOnFailureToStartOverlay() throws Exception {
        // When starting an activity fails,
        setActivityStartCode(TASK_ID, true /*taskOverlay*/, ActivityManager.START_CLASS_NOT_FOUND);

        // And the controller receives a message saying the profile is locked,
        mTaskStackListener.onTaskProfileLocked(TASK_ID, USER_ID);

        // The task the activity started in should be removed to prevent the locked task from
        // being shown.
        verifyStartActivity(TASK_ID, true /*taskOverlay*/);
        verify(mSystemServicesProxy).removeTask(TASK_ID);
    }

    // End of tests, start of helpers
    // ------------------------------

    private void setActivityStartCode(int taskId, boolean taskOverlay, int code) throws Exception {
        doReturn(code).when(mIActivityManager).startActivityAsUser(
                eq((IApplicationThread) null),
                eq((String) null),
                any(Intent.class),
                eq((String) null),
                eq((IBinder) null),
                eq((String) null),
                anyInt(),
                anyInt(),
                eq((ProfilerInfo) null),
                argThat(hasOptions(taskId, taskOverlay)),
                eq(UserHandle.USER_CURRENT));
    }

    private void verifyStartActivity(int taskId, boolean taskOverlay) throws Exception {
        verify(mIActivityManager).startActivityAsUser(
                eq((IApplicationThread) null),
                eq((String) null),
                any(Intent.class),
                eq((String) null),
                eq((IBinder) null),
                eq((String) null),
                anyInt(),
                anyInt(),
                eq((ProfilerInfo) null),
                argThat(hasOptions(taskId, taskOverlay)),
                eq(UserHandle.USER_CURRENT));
    }

    private static ArgumentMatcher<Intent> hasComponent(final Context context,
            final Class<? extends Activity> activityClass) {
        return new ArgumentMatcher<Intent>() {
            @Override
            public boolean matches(Intent intent) {
                return new ComponentName(context, activityClass).equals(intent.getComponent());
            }
        };
    }

    private static ArgumentMatcher<Bundle> hasOptions(final int taskId, final boolean overlay) {
        return new ArgumentMatcher<Bundle>() {
            @Override
            public boolean matches(Bundle item) {
                final ActivityOptions options = ActivityOptions.fromBundle(item);
                return (options.getLaunchTaskId() == taskId)
                        && (options.getTaskOverlay() == overlay);
            }
        };
    }
}