1 /*
2  * Copyright (C) 2019 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.server;
18 
19 import static org.mockito.ArgumentMatchers.any;
20 import static org.mockito.ArgumentMatchers.anyLong;
21 import static org.mockito.Mockito.doAnswer;
22 import static org.mockito.Mockito.doReturn;
23 import static org.mockito.Mockito.when;
24 
25 import android.content.Context;
26 import android.content.res.Resources;
27 import android.location.Country;
28 import android.location.CountryListener;
29 import android.location.ICountryListener;
30 import android.os.Handler;
31 import android.os.Looper;
32 import android.os.Message;
33 import android.os.RemoteException;
34 
35 import androidx.test.core.app.ApplicationProvider;
36 
37 import com.android.internal.R;
38 import com.android.server.location.countrydetector.ComprehensiveCountryDetector;
39 import com.android.server.location.countrydetector.CustomCountryDetectorTestClass;
40 
41 import com.google.common.truth.Expect;
42 
43 import org.junit.Before;
44 import org.junit.BeforeClass;
45 import org.junit.Rule;
46 import org.junit.Test;
47 import org.junit.runner.RunWith;
48 import org.mockito.Mock;
49 import org.mockito.Spy;
50 import org.mockito.junit.MockitoJUnitRunner;
51 
52 @RunWith(MockitoJUnitRunner.class)
53 public class CountryDetectorServiceTest {
54 
55     private static final String VALID_CUSTOM_TEST_CLASS =
56             "com.android.server.location.countrydetector.CustomCountryDetectorTestClass";
57     private static final String INVALID_CUSTOM_TEST_CLASS =
58             "com.android.server.location.MissingCountryDetectorTestClass";
59 
60     private static class CountryListenerTester extends ICountryListener.Stub {
61         private Country mCountry;
62 
63         @Override
onCountryDetected(Country country)64         public void onCountryDetected(Country country) {
65             mCountry = country;
66         }
67 
getCountry()68         Country getCountry() {
69             return mCountry;
70         }
71 
isNotified()72         public boolean isNotified() {
73             return mCountry != null;
74         }
75     }
76 
77     private static class CountryDetectorServiceTester extends CountryDetectorService {
78         private CountryListener mListener;
79 
CountryDetectorServiceTester(Context context, Handler handler)80         CountryDetectorServiceTester(Context context, Handler handler) {
81             super(context, handler);
82         }
83 
84         @Override
notifyReceivers(Country country)85         public void notifyReceivers(Country country) {
86             super.notifyReceivers(country);
87         }
88 
89         @Override
setCountryListener(final CountryListener listener)90         protected void setCountryListener(final CountryListener listener) {
91             mListener = listener;
92         }
93 
isListenerSet()94         boolean isListenerSet() {
95             return mListener != null;
96         }
97     }
98 
99     @Rule public final Expect expect = Expect.create();
100     @Spy private Context mContext = ApplicationProvider.getApplicationContext();
101     @Spy private Handler mHandler = new Handler(Looper.myLooper());
102     @Mock private Resources mResources;
103 
104     private CountryDetectorServiceTester mCountryDetectorService;
105 
106     @BeforeClass
oneTimeInitialization()107     public static void oneTimeInitialization() {
108         if (Looper.myLooper() == null) {
109             Looper.prepare();
110         }
111     }
112 
113     @Before
setUp()114     public void setUp() {
115         mCountryDetectorService = new CountryDetectorServiceTester(mContext, mHandler);
116 
117         // Immediately invoke run on the Runnable posted to the handler
118         doAnswer(invocation -> {
119             Message message = invocation.getArgument(0);
120             message.getCallback().run();
121             return true;
122         }).when(mHandler).sendMessageAtTime(any(Message.class), anyLong());
123 
124         doReturn(mResources).when(mContext).getResources();
125     }
126 
127     @Test
addCountryListener_validListener_listenerAdded()128     public void addCountryListener_validListener_listenerAdded() throws RemoteException {
129         CountryListenerTester countryListener = new CountryListenerTester();
130 
131         mCountryDetectorService.systemRunning();
132         expect.that(mCountryDetectorService.isListenerSet()).isFalse();
133         mCountryDetectorService.addCountryListener(countryListener);
134 
135         expect.that(mCountryDetectorService.isListenerSet()).isTrue();
136     }
137 
138     @Test
removeCountryListener_validListener_listenerRemoved()139     public void removeCountryListener_validListener_listenerRemoved() throws RemoteException {
140         CountryListenerTester countryListener = new CountryListenerTester();
141 
142         mCountryDetectorService.systemRunning();
143         mCountryDetectorService.addCountryListener(countryListener);
144         expect.that(mCountryDetectorService.isListenerSet()).isTrue();
145         mCountryDetectorService.removeCountryListener(countryListener);
146 
147         expect.that(mCountryDetectorService.isListenerSet()).isFalse();
148     }
149 
150     @Test(expected = RemoteException.class)
addCountryListener_serviceNotReady_throwsException()151     public void addCountryListener_serviceNotReady_throwsException() throws RemoteException {
152         CountryListenerTester countryListener = new CountryListenerTester();
153 
154         expect.that(mCountryDetectorService.isSystemReady()).isFalse();
155         mCountryDetectorService.addCountryListener(countryListener);
156     }
157 
158     @Test(expected = RemoteException.class)
removeCountryListener_serviceNotReady_throwsException()159     public void removeCountryListener_serviceNotReady_throwsException() throws RemoteException {
160         CountryListenerTester countryListener = new CountryListenerTester();
161 
162         expect.that(mCountryDetectorService.isSystemReady()).isFalse();
163         mCountryDetectorService.removeCountryListener(countryListener);
164     }
165 
166     @Test
detectCountry_serviceNotReady_returnNull()167     public void detectCountry_serviceNotReady_returnNull() {
168         expect.that(mCountryDetectorService.isSystemReady()).isFalse();
169 
170         expect.that(mCountryDetectorService.detectCountry()).isNull();
171     }
172 
173     @Test
notifyReceivers_twoListenersRegistered_bothNotified()174     public void notifyReceivers_twoListenersRegistered_bothNotified() throws RemoteException {
175         CountryListenerTester countryListenerA = new CountryListenerTester();
176         CountryListenerTester countryListenerB = new CountryListenerTester();
177         Country country = new Country("US", Country.COUNTRY_SOURCE_NETWORK);
178 
179         mCountryDetectorService.systemRunning();
180         mCountryDetectorService.addCountryListener(countryListenerA);
181         mCountryDetectorService.addCountryListener(countryListenerB);
182         //Immediate Callback Info support at ag/20470367
183         expect.that(countryListenerA.isNotified()).isTrue();
184         expect.that(countryListenerB.isNotified()).isTrue();
185         mCountryDetectorService.notifyReceivers(country);
186 
187         expect.that(countryListenerA.isNotified()).isTrue();
188         expect.that(countryListenerB.isNotified()).isTrue();
189         expect.that(countryListenerA.getCountry().equalsIgnoreSource(country)).isTrue();
190         expect.that(countryListenerB.getCountry().equalsIgnoreSource(country)).isTrue();
191     }
192 
193     @Test
initialize_deviceWithCustomDetector_useCustomDetectorClass()194     public void initialize_deviceWithCustomDetector_useCustomDetectorClass() {
195         when(mResources.getString(R.string.config_customCountryDetector))
196                 .thenReturn(VALID_CUSTOM_TEST_CLASS);
197 
198         mCountryDetectorService.initialize();
199 
200         expect.that(mCountryDetectorService.getCountryDetector())
201                 .isInstanceOf(CustomCountryDetectorTestClass.class);
202     }
203 
204     @Test
initialize_deviceWithInvalidCustomDetector_useDefaultDetector()205     public void initialize_deviceWithInvalidCustomDetector_useDefaultDetector() {
206         when(mResources.getString(R.string.config_customCountryDetector))
207                 .thenReturn(INVALID_CUSTOM_TEST_CLASS);
208 
209         mCountryDetectorService.initialize();
210 
211         expect.that(mCountryDetectorService.getCountryDetector())
212                 .isInstanceOf(ComprehensiveCountryDetector.class);
213     }
214 }
215