• 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
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