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 componentNameNonEmpty_showsConfirmationDialog()102 public void componentNameNonEmpty_showsConfirmationDialog() { 103 launchActivityWithValidIntent(); 104 assertThat(mActivityScenario.getState()).isAtLeast(Lifecycle.State.CREATED); 105 } 106 107 @Test onAllow_permissionMissing_finishes()108 public void onAllow_permissionMissing_finishes() throws Exception { 109 launchActivityWithValidIntent(); 110 assertThat(mActivityScenario.getState()).isAtLeast(Lifecycle.State.CREATED); 111 ServiceInfo info = createServiceInfoForTempComponent(/* permission = */ ""); 112 doReturn(info).when(mPackageManager).getServiceInfo(info.getComponentName(), 0); 113 114 AndroidMockitoHelper.syncRunOnUiThread(mActivity, () -> { 115 getConfirmationDialog().getButton(DialogInterface.BUTTON_POSITIVE).performClick(); 116 }); 117 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 118 119 // Not asserting DESTROYED state as mActivityScenario.getState() returns STARTED state for 120 // some unknown reason. 121 assertThat(mActivity.mFinishTriggered).isTrue(); 122 } 123 124 @Test onAllow_permissionAvailable_callsNotificationManager()125 public void onAllow_permissionAvailable_callsNotificationManager() throws Exception { 126 launchActivityWithValidIntent(); 127 assertThat(mActivityScenario.getState()).isAtLeast(Lifecycle.State.CREATED); 128 ServiceInfo info = createServiceInfoForTempComponent( 129 Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE); 130 doReturn(info).when(mPackageManager).getServiceInfo(info.getComponentName(), 0); 131 132 AndroidMockitoHelper.syncRunOnUiThread(mActivity, () -> { 133 getConfirmationDialog().getButton(DialogInterface.BUTTON_POSITIVE).performClick(); 134 }); 135 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 136 137 verify(mNotificationManager) 138 .setNotificationListenerAccessGranted(eq(info.getComponentName()), eq(true)); 139 } 140 141 @Test onDeny_finishes()142 public void onDeny_finishes() throws Exception { 143 launchActivityWithValidIntent(); 144 assertThat(mActivityScenario.getState()).isAtLeast(Lifecycle.State.CREATED); 145 146 AndroidMockitoHelper.syncRunOnUiThread(mActivity, () -> { 147 getConfirmationDialog().getButton(DialogInterface.BUTTON_NEGATIVE).performClick(); 148 }); 149 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 150 151 // Not asserting DESTROYED state as mActivityScenario.getState() returns STARTED state for 152 // some unknown reason. 153 assertThat(mActivity.mFinishTriggered).isTrue(); 154 } 155 getConfirmationDialog()156 private AlertDialog getConfirmationDialog() { 157 return (AlertDialog) ((ConfirmationDialogFragment) mActivity.getSupportFragmentManager() 158 .findFragmentByTag(ConfirmationDialogFragment.TAG)) 159 .getDialog(); 160 } 161 launchActivityWithValidIntent()162 private void launchActivityWithValidIntent() { 163 Intent intent = new Intent(mContext, TestActivity.class) 164 .putExtra(EXTRA_COMPONENT_NAME, TEMP_COMPONENT_NAME); 165 166 mActivityScenario = ActivityScenario.launch(intent); 167 mActivityScenario.onActivity( 168 activity -> { 169 mActivity = activity; 170 mPackageManager = mActivity.getPackageManager(); 171 mActivity.setPackageManagerSpy(mPackageManager); 172 mActivity.setNotificationManagerSpy(mNotificationManager); 173 }); 174 } 175 176 public static final class TestActivity extends NotificationAccessConfirmationActivity { 177 @Nullable private PackageManager mPackageManagerSpy; 178 @Nullable private NotificationManager mNotificationManagerSpy; 179 boolean mFinishTriggered; 180 setPackageManagerSpy(PackageManager packageManagerSpy)181 void setPackageManagerSpy(PackageManager packageManagerSpy) { 182 mPackageManagerSpy = packageManagerSpy; 183 } 184 setNotificationManagerSpy(NotificationManager notificationManagerSpy)185 void setNotificationManagerSpy(NotificationManager notificationManagerSpy) { 186 mNotificationManagerSpy = notificationManagerSpy; 187 } 188 189 @Override getPackageManager()190 public PackageManager getPackageManager() { 191 if (mPackageManagerSpy == null) { 192 mPackageManagerSpy = spy(super.getPackageManager()); 193 try { 194 ApplicationInfo info = createApplicationInfo(); 195 doReturn(info).when(mPackageManagerSpy).getApplicationInfo(any(), any()); 196 } catch (PackageManager.NameNotFoundException e) { 197 // do nothing... tests will fail when activity finishes during onCreate() 198 } 199 } 200 return mPackageManagerSpy; 201 } 202 203 @Override getSystemService(String name)204 public Object getSystemService(String name) { 205 return mNotificationManagerSpy != null 206 && name.toLowerCase(Locale.ROOT).contains("notification") 207 ? mNotificationManagerSpy 208 : super.getSystemService(name); 209 } 210 211 @Override finish()212 public void finish() { 213 mFinishTriggered = true; 214 super.finish(); 215 } 216 } 217 } 218