1/*
2 * Copyright (C) 2017 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 */
16package com.android.settingslib;
17
18import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
19
20import static com.android.settingslib.Utils.STORAGE_MANAGER_SHOW_OPT_IN_PROPERTY;
21
22import static com.google.common.truth.Truth.assertThat;
23import static org.mockito.ArgumentMatchers.argThat;
24import static org.mockito.Matchers.eq;
25import static org.mockito.Mockito.mock;
26import static org.mockito.Mockito.spy;
27import static org.mockito.Mockito.verify;
28import static org.mockito.Mockito.when;
29import static org.robolectric.Shadows.shadowOf;
30
31import android.app.ActivityManager;
32import android.content.ContentResolver;
33import android.content.Context;
34import android.content.Intent;
35import android.content.res.Resources;
36import android.location.LocationManager;
37import android.media.AudioManager;
38import android.os.SystemProperties;
39import android.os.UserHandle;
40import android.provider.Settings;
41import android.provider.Settings.Secure;
42import android.text.TextUtils;
43
44import com.android.settingslib.wrapper.LocationManagerWrapper;
45
46import org.junit.Before;
47import org.junit.Test;
48import org.junit.runner.RunWith;
49import org.mockito.ArgumentMatcher;
50import org.mockito.ArgumentMatchers;
51import org.mockito.Mock;
52import org.mockito.MockitoAnnotations;
53import org.robolectric.RuntimeEnvironment;
54import org.robolectric.annotation.Config;
55import org.robolectric.annotation.Implementation;
56import org.robolectric.annotation.Implements;
57import org.robolectric.shadows.ShadowAudioManager;
58import org.robolectric.shadows.ShadowSettings;
59
60import java.util.HashMap;
61import java.util.Map;
62
63@RunWith(SettingsLibRobolectricTestRunner.class)
64@Config(shadows = {
65            UtilsTest.ShadowSecure.class,
66            UtilsTest.ShadowLocationManagerWrapper.class})
67public class UtilsTest {
68    private static final double[] TEST_PERCENTAGES = {0, 0.4, 0.5, 0.6, 49, 49.3, 49.8, 50, 100};
69    private static final String PERCENTAGE_0 = "0%";
70    private static final String PERCENTAGE_1 = "1%";
71    private static final String PERCENTAGE_49 = "49%";
72    private static final String PERCENTAGE_50 = "50%";
73    private static final String PERCENTAGE_100 = "100%";
74
75    private ShadowAudioManager mShadowAudioManager;
76    private Context mContext;
77    @Mock
78    private LocationManager mLocationManager;
79
80    @Before
81    public void setUp() {
82        MockitoAnnotations.initMocks(this);
83        mContext = spy(RuntimeEnvironment.application);
84        when(mContext.getSystemService(Context.LOCATION_SERVICE)).thenReturn(mLocationManager);
85        ShadowSecure.reset();
86        mShadowAudioManager = shadowOf(mContext.getSystemService(AudioManager.class));
87    }
88
89    @Test
90    public void testUpdateLocationMode_sendBroadcast() {
91        int currentUserId = ActivityManager.getCurrentUser();
92        Utils.updateLocationMode(
93                mContext,
94                Secure.LOCATION_MODE_OFF,
95                Secure.LOCATION_MODE_HIGH_ACCURACY,
96                currentUserId,
97                Settings.Secure.LOCATION_CHANGER_QUICK_SETTINGS);
98
99        verify(mContext).sendBroadcastAsUser(
100                argThat(actionMatches(LocationManager.MODE_CHANGING_ACTION)),
101                ArgumentMatchers.eq(UserHandle.of(currentUserId)),
102                ArgumentMatchers.eq(WRITE_SECURE_SETTINGS));
103        assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
104                Settings.Secure.LOCATION_CHANGER, Settings.Secure.LOCATION_CHANGER_UNKNOWN))
105                .isEqualTo(Settings.Secure.LOCATION_CHANGER_QUICK_SETTINGS);
106    }
107
108    @Test
109    public void testUpdateLocationEnabled_sendBroadcast() {
110        int currentUserId = ActivityManager.getCurrentUser();
111        Utils.updateLocationEnabled(mContext, true, currentUserId,
112                Settings.Secure.LOCATION_CHANGER_QUICK_SETTINGS);
113
114        verify(mContext).sendBroadcastAsUser(
115            argThat(actionMatches(LocationManager.MODE_CHANGING_ACTION)),
116            ArgumentMatchers.eq(UserHandle.of(currentUserId)),
117            ArgumentMatchers.eq(WRITE_SECURE_SETTINGS));
118        assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
119                Settings.Secure.LOCATION_CHANGER, Settings.Secure.LOCATION_CHANGER_UNKNOWN))
120                .isEqualTo(Settings.Secure.LOCATION_CHANGER_QUICK_SETTINGS);
121    }
122
123    @Test
124    public void testFormatPercentage_RoundTrue_RoundUpIfPossible() {
125        final String[] expectedPercentages = {PERCENTAGE_0, PERCENTAGE_0, PERCENTAGE_1,
126                PERCENTAGE_1, PERCENTAGE_49, PERCENTAGE_49, PERCENTAGE_50, PERCENTAGE_50,
127                PERCENTAGE_100};
128
129        for (int i = 0, size = TEST_PERCENTAGES.length; i < size; i++) {
130            final String percentage = Utils.formatPercentage(TEST_PERCENTAGES[i], true);
131            assertThat(percentage).isEqualTo(expectedPercentages[i]);
132        }
133    }
134
135    @Test
136    public void testFormatPercentage_RoundFalse_NoRound() {
137        final String[] expectedPercentages = {PERCENTAGE_0, PERCENTAGE_0, PERCENTAGE_0,
138                PERCENTAGE_0, PERCENTAGE_49, PERCENTAGE_49, PERCENTAGE_49, PERCENTAGE_50,
139                PERCENTAGE_100};
140
141        for (int i = 0, size = TEST_PERCENTAGES.length; i < size; i++) {
142            final String percentage = Utils.formatPercentage(TEST_PERCENTAGES[i], false);
143            assertThat(percentage).isEqualTo(expectedPercentages[i]);
144        }
145    }
146
147    @Test
148    public void testGetDefaultStorageManagerDaysToRetain_storageManagerDaysToRetainUsesResources() {
149        Resources resources = mock(Resources.class);
150        when(resources.getInteger(
151                        eq(
152                                com.android
153                                        .internal
154                                        .R
155                                        .integer
156                                        .config_storageManagerDaystoRetainDefault)))
157                .thenReturn(60);
158        assertThat(Utils.getDefaultStorageManagerDaysToRetain(resources)).isEqualTo(60);
159    }
160
161    @Test
162    public void testIsStorageManagerEnabled_UsesSystemProperties() {
163        SystemProperties.set(STORAGE_MANAGER_SHOW_OPT_IN_PROPERTY, "false");
164        assertThat(Utils.isStorageManagerEnabled(mContext)).isTrue();
165    }
166
167    private static ArgumentMatcher<Intent> actionMatches(String expected) {
168        return intent -> TextUtils.equals(expected, intent.getAction());
169    }
170
171    @Implements(value = Settings.Secure.class)
172    public static class ShadowSecure extends ShadowSettings.ShadowSecure {
173        private static Map<String, Integer> map = new HashMap<>();
174
175        @Implementation
176        public static boolean putIntForUser(ContentResolver cr, String name, int value, int userHandle) {
177            map.put(name, value);
178            return true;
179        }
180
181        @Implementation
182        public static int getIntForUser(ContentResolver cr, String name, int def, int userHandle) {
183            if (map.containsKey(name)) {
184                return map.get(name);
185            } else {
186                return def;
187            }
188        }
189
190        public static void reset() {
191            map.clear();
192        }
193    }
194
195    @Implements(value = LocationManagerWrapper.class)
196    public static class ShadowLocationManagerWrapper {
197
198        @Implementation
199        public void setLocationEnabledForUser(boolean enabled, UserHandle userHandle) {
200            // Do nothing
201        }
202    }
203
204    @Test
205    public void isAudioModeOngoingCall_modeInCommunication_returnTrue() {
206        mShadowAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
207
208        assertThat(Utils.isAudioModeOngoingCall(mContext)).isTrue();
209    }
210
211    @Test
212    public void isAudioModeOngoingCall_modeInCall_returnTrue() {
213        mShadowAudioManager.setMode(AudioManager.MODE_IN_CALL);
214
215        assertThat(Utils.isAudioModeOngoingCall(mContext)).isTrue();
216    }
217
218    @Test
219    public void isAudioModeOngoingCall_modeRingtone_returnTrue() {
220        mShadowAudioManager.setMode(AudioManager.MODE_RINGTONE);
221
222        assertThat(Utils.isAudioModeOngoingCall(mContext)).isTrue();
223    }
224
225    @Test
226    public void isAudioModeOngoingCall_modeNormal_returnFalse() {
227        mShadowAudioManager.setMode(AudioManager.MODE_NORMAL);
228
229        assertThat(Utils.isAudioModeOngoingCall(mContext)).isFalse();
230    }
231}
232