1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.car.settings.notifications; 18 19 import static com.android.internal.notification.NotificationAccessConfirmationActivityContract.EXTRA_COMPONENT_NAME; 20 21 import static com.google.common.truth.Truth.assertThat; 22 23 import static org.mockito.ArgumentMatchers.any; 24 import static org.mockito.Mockito.doReturn; 25 import static org.mockito.Mockito.eq; 26 import static org.mockito.Mockito.spy; 27 import static org.mockito.Mockito.verify; 28 29 import android.Manifest; 30 import android.app.AlertDialog; 31 import android.app.NotificationManager; 32 import android.car.test.mocks.AndroidMockitoHelper; 33 import android.content.ComponentName; 34 import android.content.Context; 35 import android.content.DialogInterface; 36 import android.content.Intent; 37 import android.content.pm.ApplicationInfo; 38 import android.content.pm.PackageManager; 39 import android.content.pm.ServiceInfo; 40 41 import androidx.annotation.Nullable; 42 import androidx.lifecycle.Lifecycle; 43 import androidx.test.core.app.ActivityScenario; 44 import androidx.test.core.app.ApplicationProvider; 45 import androidx.test.ext.junit.runners.AndroidJUnit4; 46 import androidx.test.platform.app.InstrumentationRegistry; 47 48 import com.android.car.settings.common.ConfirmationDialogFragment; 49 50 import org.junit.Before; 51 import org.junit.Test; 52 import org.junit.runner.RunWith; 53 import org.mockito.Mock; 54 import org.mockito.MockitoAnnotations; 55 56 import java.util.Locale; 57 58 /** 59 * Unit tests for {@link NotificationAccessConfirmationActivity} 60 */ 61 @RunWith(AndroidJUnit4.class) 62 public final class NotificationAccessConfirmationActivityTest { 63 private static final ComponentName TEMP_COMPONENT_NAME = ComponentName.unflattenFromString( 64 "com.temp/com.temp.k"); 65 66 private final Context mContext = ApplicationProvider.getApplicationContext(); 67 private ActivityScenario<TestActivity> mActivityScenario; 68 private TestActivity mActivity; 69 private PackageManager mPackageManager; 70 71 @Mock 72 private NotificationManager mNotificationManager; 73 createServiceInfoForTempComponent(String permission)74 private static ServiceInfo createServiceInfoForTempComponent(String permission) { 75 ServiceInfo info = new ServiceInfo(); 76 info.name = TEMP_COMPONENT_NAME.getClassName(); 77 info.packageName = TEMP_COMPONENT_NAME.getPackageName(); 78 info.permission = permission; 79 return info; 80 } 81 createApplicationInfo()82 private static ApplicationInfo createApplicationInfo() { 83 ApplicationInfo info = new ApplicationInfo(); 84 info.name = TEMP_COMPONENT_NAME.getClassName(); 85 info.packageName = TEMP_COMPONENT_NAME.getPackageName(); 86 return info; 87 } 88 89 @Before setup()90 public void setup() { 91 MockitoAnnotations.initMocks(this); 92 } 93 94 @Test componentNameEmpty_finishes()95 public void componentNameEmpty_finishes() { 96 Intent intent = new Intent(mContext, TestActivity.class); 97 mActivityScenario = ActivityScenario.launch(intent); 98 assertThat(mActivityScenario.getState()).isEqualTo(Lifecycle.State.DESTROYED); 99 } 100 101 @Test showsDialog()102 public void showsDialog() throws Exception { 103 ServiceInfo info = createServiceInfoForTempComponent( 104 Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE); 105 doReturn(info).when(mPackageManager).getServiceInfo(info.getComponentName(), 0); 106 107 launchActivityWithValidIntent(); 108 assertThat(mActivityScenario.getState()).isAtLeast(Lifecycle.State.CREATED); 109 assertThat(mActivity.mFinishTriggered).isFalse(); 110 assertThat(getConfirmationDialog().isShowing()).isTrue(); 111 } 112 113 @Test intentFilterMissing_finishes()114 public void intentFilterMissing_finishes() { 115 launchActivityWithValidIntent(); 116 assertThat(mActivity.mFinishTriggered).isTrue(); 117 } 118 119 @Test permissionMissing_finishes()120 public void permissionMissing_finishes() throws Exception { 121 ServiceInfo info = createServiceInfoForTempComponent(/* permission = */ ""); 122 doReturn(info).when(mPackageManager).getServiceInfo(info.getComponentName(), 0); 123 124 launchActivityWithValidIntent(); 125 assertThat(mActivity.mFinishTriggered).isTrue(); 126 } 127 128 @Test onAllow_permissionAvailable_callsNotificationManager()129 public void onAllow_permissionAvailable_callsNotificationManager() throws Exception { 130 launchActivityWithValidIntent(); 131 assertThat(mActivityScenario.getState()).isAtLeast(Lifecycle.State.CREATED); 132 ServiceInfo info = createServiceInfoForTempComponent( 133 Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE); 134 doReturn(info).when(mPackageManager).getServiceInfo(info.getComponentName(), 0); 135 136 AndroidMockitoHelper.syncRunOnUiThread(mActivity, () -> { 137 getConfirmationDialog().getButton(DialogInterface.BUTTON_POSITIVE).performClick(); 138 }); 139 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 140 141 verify(mNotificationManager) 142 .setNotificationListenerAccessGranted(eq(info.getComponentName()), eq(true)); 143 } 144 145 @Test onDeny_finishes()146 public void onDeny_finishes() throws Exception { 147 launchActivityWithValidIntent(); 148 assertThat(mActivityScenario.getState()).isAtLeast(Lifecycle.State.CREATED); 149 150 AndroidMockitoHelper.syncRunOnUiThread(mActivity, () -> { 151 getConfirmationDialog().getButton(DialogInterface.BUTTON_NEGATIVE).performClick(); 152 }); 153 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 154 155 // Not asserting DESTROYED state as mActivityScenario.getState() returns STARTED state for 156 // some unknown reason. 157 assertThat(mActivity.mFinishTriggered).isTrue(); 158 } 159 getConfirmationDialog()160 private AlertDialog getConfirmationDialog() { 161 return (AlertDialog) ((ConfirmationDialogFragment) mActivity.getSupportFragmentManager() 162 .findFragmentByTag(ConfirmationDialogFragment.TAG)) 163 .getDialog(); 164 } 165 launchActivityWithValidIntent()166 private void launchActivityWithValidIntent() { 167 Intent intent = new Intent(mContext, TestActivity.class) 168 .putExtra(EXTRA_COMPONENT_NAME, TEMP_COMPONENT_NAME); 169 170 mActivityScenario = ActivityScenario.launch(intent); 171 mActivityScenario.onActivity( 172 activity -> { 173 mActivity = activity; 174 mPackageManager = mActivity.getPackageManager(); 175 mActivity.setPackageManagerSpy(mPackageManager); 176 mActivity.setNotificationManagerSpy(mNotificationManager); 177 }); 178 } 179 180 public static final class TestActivity extends NotificationAccessConfirmationActivity { 181 @Nullable private PackageManager mPackageManagerSpy; 182 @Nullable private NotificationManager mNotificationManagerSpy; 183 boolean mFinishTriggered; 184 setPackageManagerSpy(PackageManager packageManagerSpy)185 void setPackageManagerSpy(PackageManager packageManagerSpy) { 186 mPackageManagerSpy = packageManagerSpy; 187 } 188 setNotificationManagerSpy(NotificationManager notificationManagerSpy)189 void setNotificationManagerSpy(NotificationManager notificationManagerSpy) { 190 mNotificationManagerSpy = notificationManagerSpy; 191 } 192 193 @Override getPackageManager()194 public PackageManager getPackageManager() { 195 if (mPackageManagerSpy == null) { 196 mPackageManagerSpy = spy(super.getPackageManager()); 197 try { 198 ApplicationInfo info = createApplicationInfo(); 199 doReturn(info).when(mPackageManagerSpy).getApplicationInfo(any(), any()); 200 } catch (PackageManager.NameNotFoundException e) { 201 // do nothing... tests will fail when activity finishes during onCreate() 202 } 203 } 204 return mPackageManagerSpy; 205 } 206 207 @Override getSystemService(String name)208 public Object getSystemService(String name) { 209 return mNotificationManagerSpy != null 210 && name.toLowerCase(Locale.ROOT).contains("notification") 211 ? mNotificationManagerSpy 212 : super.getSystemService(name); 213 } 214 215 @Override finish()216 public void finish() { 217 mFinishTriggered = true; 218 super.finish(); 219 } 220 } 221 } 222