• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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