blob: d751ac818c22d825d7f00a9e70be4b1f1df5479e [file] [log] [blame]
/*
* Copyright (C) 2021 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.systembar;
import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
import static android.hardware.SensorPrivacyManager.Sources.QS_TILE;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.car.Car;
import android.car.user.CarUserManager;
import android.content.BroadcastReceiver;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.SensorPrivacyManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
import androidx.test.filters.SmallTest;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.car.CarDeviceProvisionedController;
import com.android.systemui.car.CarServiceProvider;
import com.android.systemui.car.CarSystemUiTest;
import com.android.systemui.car.privacy.MicPrivacyChip;
import com.android.systemui.privacy.PrivacyItem;
import com.android.systemui.privacy.PrivacyItemController;
import com.android.systemui.privacy.PrivacyType;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.Collections;
import java.util.concurrent.Executor;
@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
public class PrivacyChipViewControllerTest extends SysuiTestCase {
private PrivacyChipViewController mPrivacyChipViewController;
private FrameLayout mFrameLayout;
private MicPrivacyChip mMicPrivacyChip;
@Captor
private ArgumentCaptor<Runnable> mRunnableArgumentCaptor;
@Captor
private ArgumentCaptor<PrivacyItemController.Callback> mPicCallbackArgumentCaptor;
@Captor
private ArgumentCaptor<SensorPrivacyManager.OnSensorPrivacyChangedListener>
mOnSensorPrivacyChangedListenerArgumentCaptor;
@Captor
private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverArgumentCaptor;
@Captor
private ArgumentCaptor<CarUserManager.UserLifecycleListener>
mUserLifecycleListenerArgumentCaptor;
@Mock
private PrivacyItemController mPrivacyItemController;
@Mock
private PrivacyItem mPrivacyItem;
@Mock
private Executor mExecutor;
@Mock
private BroadcastDispatcher mBroadcastDispatcher;
@Mock
private SensorPrivacyManager mSensorPrivacyManager;
@Mock
private CarDeviceProvisionedController mCarDeviceProvisionedController;
@Mock
private CarUserManager mCarUserManager;
@Mock
private Car mCar;
@Mock
private Runnable mQsTileNotifyUpdateRunnable;
@Before
public void setUp() {
MockitoAnnotations.initMocks(/* testClass= */ this);
mFrameLayout = new FrameLayout(mContext);
mMicPrivacyChip = spy((MicPrivacyChip) LayoutInflater.from(mContext)
.inflate(R.layout.mic_privacy_chip, /* root= */ null));
mFrameLayout.addView(mMicPrivacyChip);
mContext = spy(mContext);
when(mContext.getMainExecutor()).thenReturn(mExecutor);
when(mCar.isConnected()).thenReturn(true);
when(mCar.getCarManager(Car.CAR_USER_SERVICE)).thenReturn(mCarUserManager);
CarServiceProvider carServiceProvider = new CarServiceProvider(mContext, mCar);
mPrivacyChipViewController = new PrivacyChipViewController(mContext, mPrivacyItemController,
carServiceProvider, mBroadcastDispatcher, mSensorPrivacyManager,
mCarDeviceProvisionedController);
when(mCarDeviceProvisionedController.getCurrentUser()).thenReturn(0);
}
@Test
public void addPrivacyChipView_privacyChipViewPresent_addCallbackCalled() {
mPrivacyChipViewController.addPrivacyChipView(mFrameLayout);
verify(mPrivacyItemController).addCallback(any());
}
@Test
public void addPrivacyChipView_privacyChipViewPresent_micStatusSet() {
when(mSensorPrivacyManager.isSensorPrivacyEnabled(MICROPHONE, /* userId= */ 0))
.thenReturn(false);
mPrivacyChipViewController.addPrivacyChipView(mFrameLayout);
verify(mExecutor).execute(mRunnableArgumentCaptor.capture());
mRunnableArgumentCaptor.getValue().run();
verify(mMicPrivacyChip).setMicrophoneEnabled(eq(true));
}
@Test
public void addPrivacyChipView_privacyChipViewNotPresent_addCallbackNotCalled() {
mPrivacyChipViewController.addPrivacyChipView(new View(getContext()));
verify(mPrivacyItemController, never()).addCallback(any());
}
@Test
public void onPrivacyItemsChanged_micIsPartOfPrivacyItems_animateInCalled() {
when(mPrivacyItem.getPrivacyType()).thenReturn(PrivacyType.TYPE_MICROPHONE);
mPrivacyChipViewController.addPrivacyChipView(mFrameLayout);
verify(mPrivacyItemController).addCallback(mPicCallbackArgumentCaptor.capture());
mPicCallbackArgumentCaptor.getValue().onFlagAllChanged(true);
mPicCallbackArgumentCaptor.getValue().onFlagMicCameraChanged(true);
mPicCallbackArgumentCaptor.getValue()
.onPrivacyItemsChanged(Collections.singletonList(mPrivacyItem));
verify(mExecutor, times(2)).execute(mRunnableArgumentCaptor.capture());
mRunnableArgumentCaptor.getAllValues().forEach(Runnable::run);
verify(mMicPrivacyChip).animateIn();
}
@Test
public void onPrivacyItemsChanged_micIsPartOfPrivacyItemsTwice_animateInCalledOnce() {
when(mPrivacyItem.getPrivacyType()).thenReturn(PrivacyType.TYPE_MICROPHONE);
mPrivacyChipViewController.addPrivacyChipView(mFrameLayout);
verify(mPrivacyItemController).addCallback(mPicCallbackArgumentCaptor.capture());
mPicCallbackArgumentCaptor.getValue().onFlagAllChanged(true);
mPicCallbackArgumentCaptor.getValue().onFlagMicCameraChanged(true);
mPicCallbackArgumentCaptor.getValue()
.onPrivacyItemsChanged(Collections.singletonList(mPrivacyItem));
mPicCallbackArgumentCaptor.getValue()
.onPrivacyItemsChanged(Collections.singletonList(mPrivacyItem));
verify(mExecutor, times(2)).execute(mRunnableArgumentCaptor.capture());
mRunnableArgumentCaptor.getAllValues().forEach(Runnable::run);
verify(mMicPrivacyChip).animateIn();
}
@Test
public void onPrivacyItemsChanged_micIsNotPartOfPrivacyItems_animateOutCalled() {
when(mPrivacyItem.getPrivacyType()).thenReturn(PrivacyType.TYPE_MICROPHONE);
mPrivacyChipViewController.addPrivacyChipView(mFrameLayout);
verify(mPrivacyItemController).addCallback(mPicCallbackArgumentCaptor.capture());
mPicCallbackArgumentCaptor.getValue().onFlagAllChanged(true);
mPicCallbackArgumentCaptor.getValue().onFlagMicCameraChanged(true);
mPicCallbackArgumentCaptor.getValue()
.onPrivacyItemsChanged(Collections.singletonList(mPrivacyItem));
mPicCallbackArgumentCaptor.getValue().onPrivacyItemsChanged(Collections.emptyList());
verify(mExecutor, times(3))
.execute(mRunnableArgumentCaptor.capture());
mRunnableArgumentCaptor.getAllValues().forEach(Runnable::run);
verify(mMicPrivacyChip).animateOut();
}
@Test
public void onPrivacyItemsChanged_micIsNotPartOfPrivacyItemsTwice_animateOutCalledOnce() {
when(mPrivacyItem.getPrivacyType()).thenReturn(PrivacyType.TYPE_MICROPHONE);
mPrivacyChipViewController.addPrivacyChipView(mFrameLayout);
verify(mPrivacyItemController).addCallback(mPicCallbackArgumentCaptor.capture());
mPicCallbackArgumentCaptor.getValue().onFlagAllChanged(true);
mPicCallbackArgumentCaptor.getValue().onFlagMicCameraChanged(true);
mPicCallbackArgumentCaptor.getValue()
.onPrivacyItemsChanged(Collections.singletonList(mPrivacyItem));
mPicCallbackArgumentCaptor.getValue().onPrivacyItemsChanged(Collections.emptyList());
mPicCallbackArgumentCaptor.getValue().onPrivacyItemsChanged(Collections.emptyList());
verify(mExecutor, times(3))
.execute(mRunnableArgumentCaptor.capture());
mRunnableArgumentCaptor.getAllValues().forEach(Runnable::run);
verify(mMicPrivacyChip).animateOut();
}
@Test
public void onPrivacyItemsChanged_qsTileNotifyUpdateRunnableExecuted() {
when(mPrivacyItem.getPrivacyType()).thenReturn(PrivacyType.TYPE_MICROPHONE);
mPrivacyChipViewController.setNotifyUpdateRunnable(mQsTileNotifyUpdateRunnable);
mPrivacyChipViewController.addPrivacyChipView(mFrameLayout);
verify(mPrivacyItemController).addCallback(mPicCallbackArgumentCaptor.capture());
mPicCallbackArgumentCaptor.getValue().onFlagAllChanged(true);
mPicCallbackArgumentCaptor.getValue().onFlagMicCameraChanged(true);
mPicCallbackArgumentCaptor.getValue().onPrivacyItemsChanged(Collections.emptyList());
verify(mExecutor).execute(mRunnableArgumentCaptor.capture());
mRunnableArgumentCaptor.getAllValues().forEach(Runnable::run);
verify(mQsTileNotifyUpdateRunnable).run();
}
@Test
public void onSensorPrivacyChanged_argTrue_setMicrophoneEnabledWithFalseCalled() {
mPrivacyChipViewController.addPrivacyChipView(mFrameLayout);
verify(mSensorPrivacyManager).addSensorPrivacyListener(eq(MICROPHONE),
/* userId= */ eq(0), mOnSensorPrivacyChangedListenerArgumentCaptor.capture());
reset(mMicPrivacyChip);
reset(mExecutor);
mOnSensorPrivacyChangedListenerArgumentCaptor.getValue()
.onSensorPrivacyChanged(MICROPHONE, /* enabled= */ true);
verify(mExecutor).execute(mRunnableArgumentCaptor.capture());
mRunnableArgumentCaptor.getAllValues().forEach(Runnable::run);
verify(mMicPrivacyChip).setMicrophoneEnabled(eq(false));
}
@Test
public void onSensorPrivacyChanged_argFalse_setMicrophoneEnabledWithTrueCalled() {
mPrivacyChipViewController.addPrivacyChipView(mFrameLayout);
verify(mSensorPrivacyManager).addSensorPrivacyListener(eq(MICROPHONE),
/* userId= */ eq(0), mOnSensorPrivacyChangedListenerArgumentCaptor.capture());
reset(mMicPrivacyChip);
reset(mExecutor);
mOnSensorPrivacyChangedListenerArgumentCaptor.getValue()
.onSensorPrivacyChanged(MICROPHONE, /* enabled= */ false);
verify(mExecutor).execute(mRunnableArgumentCaptor.capture());
mRunnableArgumentCaptor.getAllValues().forEach(Runnable::run);
verify(mMicPrivacyChip).setMicrophoneEnabled(eq(true));
}
@Test
public void onSensorPrivacyChanged_qsTileNotifyUpdateRunnableExecuted() {
mPrivacyChipViewController.setNotifyUpdateRunnable(mQsTileNotifyUpdateRunnable);
mPrivacyChipViewController.addPrivacyChipView(mFrameLayout);
verify(mSensorPrivacyManager).addSensorPrivacyListener(eq(MICROPHONE),
/* userId= */ eq(0), mOnSensorPrivacyChangedListenerArgumentCaptor.capture());
reset(mMicPrivacyChip);
reset(mExecutor);
mOnSensorPrivacyChangedListenerArgumentCaptor.getValue()
.onSensorPrivacyChanged(MICROPHONE, /* enabled= */ true);
verify(mExecutor).execute(mRunnableArgumentCaptor.capture());
mRunnableArgumentCaptor.getAllValues().forEach(Runnable::run);
verify(mQsTileNotifyUpdateRunnable).run();
}
@Test
public void onUserUpdateReceive_setMicrophoneEnabledCalled() {
when(mSensorPrivacyManager.isSensorPrivacyEnabled(MICROPHONE, /* userId= */ 1))
.thenReturn(true);
mPrivacyChipViewController.addPrivacyChipView(mFrameLayout);
verify(mBroadcastDispatcher).registerReceiver(mBroadcastReceiverArgumentCaptor.capture(),
argThat((IntentFilter filter) -> filter.hasAction(Intent.ACTION_USER_INFO_CHANGED)),
any(), any());
reset(mExecutor);
when(mCarDeviceProvisionedController.getCurrentUser()).thenReturn(1);
mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext,
new Intent(Intent.ACTION_USER_INFO_CHANGED));
verify(mExecutor).execute(mRunnableArgumentCaptor.capture());
mRunnableArgumentCaptor.getValue().run();
verify(mMicPrivacyChip).setMicrophoneEnabled(eq(false));
}
@Test
public void onUserChangeReceive_setMicrophoneEnabledCalled() {
when(mSensorPrivacyManager.isSensorPrivacyEnabled(MICROPHONE, /* userId= */ 1))
.thenReturn(false);
mPrivacyChipViewController.addPrivacyChipView(mFrameLayout);
verify(mBroadcastDispatcher).registerReceiver(mBroadcastReceiverArgumentCaptor.capture(),
argThat((IntentFilter filter) -> filter.hasAction(Intent.ACTION_USER_FOREGROUND)),
any(), any());
reset(mExecutor);
when(mCarDeviceProvisionedController.getCurrentUser()).thenReturn(1);
mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext,
new Intent(Intent.ACTION_USER_FOREGROUND));
verify(mExecutor).execute(mRunnableArgumentCaptor.capture());
mRunnableArgumentCaptor.getValue().run();
verify(mMicPrivacyChip).setMicrophoneEnabled(eq(true));
}
@Test
public void isMicEnabled_sensorPrivacyEnabled_returnFalse() {
when(mSensorPrivacyManager.isSensorPrivacyEnabled(eq(MICROPHONE), anyInt()))
.thenReturn(true);
assertThat(mPrivacyChipViewController.isMicEnabled()).isFalse();
}
@Test
public void isMicEnabled_sensorPrivacyDisabled_returnTrue() {
when(mSensorPrivacyManager.isSensorPrivacyEnabled(eq(MICROPHONE), anyInt()))
.thenReturn(false);
assertThat(mPrivacyChipViewController.isMicEnabled()).isTrue();
}
@Test
public void toggleMic_micTurnedOn_sensorPrivacyEnabled() {
when(mSensorPrivacyManager.isSensorPrivacyEnabled(eq(MICROPHONE), anyInt()))
.thenReturn(false);
mPrivacyChipViewController.toggleMic();
verify(mSensorPrivacyManager)
.setSensorPrivacy(eq(QS_TILE), eq(MICROPHONE), eq(true), anyInt());
}
@Test
public void toggleMic_micTurnedOff_sensorPrivacyDisabled() {
when(mSensorPrivacyManager.isSensorPrivacyEnabled(eq(MICROPHONE), anyInt()))
.thenReturn(true);
mPrivacyChipViewController.toggleMic();
verify(mSensorPrivacyManager)
.setSensorPrivacy(eq(QS_TILE), eq(MICROPHONE), eq(false), anyInt());
}
}