1 /* 2 * Copyright (C) 2022 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.ons; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertTrue; 22 import static org.mockito.ArgumentMatchers.eq; 23 import static org.mockito.Mockito.doReturn; 24 25 import android.content.Context; 26 import android.content.SharedPreferences; 27 import android.telephony.SubscriptionInfo; 28 import android.telephony.SubscriptionManager; 29 30 import com.android.ons.ONSProfileActivator.Result; 31 import com.android.ons.ONSProfileDownloader.DownloadRetryResultCode; 32 33 import org.junit.After; 34 import org.junit.Before; 35 import org.junit.Test; 36 import org.junit.runner.RunWith; 37 import org.junit.runners.JUnit4; 38 import org.mockito.Mock; 39 import org.mockito.MockitoAnnotations; 40 import org.mockito.Spy; 41 42 import java.util.HashMap; 43 import java.util.Map; 44 import java.util.Set; 45 46 @RunWith(JUnit4.class) 47 public class ONSStatsTest { 48 49 private static final String ONS_ATOM_LOG_FILE = "ons_atom_log_info"; 50 private static final String KEY_DETAILED_ERROR_CODE = "_detailed_error_code"; 51 52 @Spy private Context mContext; 53 @Mock private SubscriptionManager mSubscriptionManager; 54 private SharedPreferences mSharedPreferences; 55 @Mock private SubscriptionInfo mSubInfoId1; 56 @Mock private SubscriptionInfo mSubInfoId2; 57 private ONSStats mONSStats; 58 59 private class FakeSharedPreferences implements SharedPreferences { 60 HashMap<String, Object> mMap = new HashMap<>(); 61 62 @Override getAll()63 public Map<String, ?> getAll() { 64 return mMap; 65 } 66 67 @Override getString(String key, String defValue)68 public String getString(String key, String defValue) { 69 return (String) mMap.getOrDefault(key, defValue); 70 } 71 72 @Override getStringSet(String key, Set<String> defValues)73 public Set<String> getStringSet(String key, Set<String> defValues) { 74 if (mMap.containsKey(key)) { 75 return (Set<String>) mMap.get(key); 76 } 77 return defValues; 78 } 79 80 @Override getInt(String key, int defValue)81 public int getInt(String key, int defValue) { 82 return (int) mMap.getOrDefault(key, defValue); 83 } 84 85 @Override getLong(String key, long defValue)86 public long getLong(String key, long defValue) { 87 return 0; // not used 88 } 89 90 @Override getFloat(String key, float defValue)91 public float getFloat(String key, float defValue) { 92 return (float) mMap.getOrDefault(key, defValue); 93 } 94 95 @Override getBoolean(String key, boolean defValue)96 public boolean getBoolean(String key, boolean defValue) { 97 return (boolean) mMap.getOrDefault(key, defValue); 98 } 99 100 @Override contains(String key)101 public boolean contains(String key) { 102 return mMap.containsKey(key); 103 } 104 105 @Override edit()106 public Editor edit() { 107 TestEditor editor = new TestEditor(); 108 editor.map = mMap; 109 return editor; 110 } 111 112 @Override registerOnSharedPreferenceChangeListener( OnSharedPreferenceChangeListener listener)113 public void registerOnSharedPreferenceChangeListener( 114 OnSharedPreferenceChangeListener listener) {} 115 116 @Override unregisterOnSharedPreferenceChangeListener( OnSharedPreferenceChangeListener listener)117 public void unregisterOnSharedPreferenceChangeListener( 118 OnSharedPreferenceChangeListener listener) {} 119 120 private class TestEditor implements SharedPreferences.Editor { 121 HashMap<String, Object> map = new HashMap<>(); 122 123 @Override putString(String key, String value)124 public SharedPreferences.Editor putString(String key, String value) { 125 map.put(key, value); 126 return this; 127 } 128 129 @Override putStringSet(String key, Set<String> values)130 public SharedPreferences.Editor putStringSet(String key, Set<String> values) { 131 map.put(key, values); 132 return this; 133 } 134 135 @Override putInt(String key, int value)136 public SharedPreferences.Editor putInt(String key, int value) { 137 map.put(key, value); 138 return this; 139 } 140 141 @Override putLong(String key, long value)142 public SharedPreferences.Editor putLong(String key, long value) { 143 map.put(key, value); 144 return this; 145 } 146 147 @Override putFloat(String key, float value)148 public SharedPreferences.Editor putFloat(String key, float value) { 149 map.put(key, value); 150 return this; 151 } 152 153 @Override putBoolean(String key, boolean value)154 public SharedPreferences.Editor putBoolean(String key, boolean value) { 155 map.put(key, value); 156 return this; 157 } 158 159 @Override remove(String key)160 public SharedPreferences.Editor remove(String key) { 161 map.remove(key); 162 return this; 163 } 164 165 @Override clear()166 public SharedPreferences.Editor clear() { 167 map.clear(); 168 return this; 169 } 170 171 @Override commit()172 public boolean commit() { 173 mMap = map; 174 return true; 175 } 176 177 @Override apply()178 public void apply() { 179 mMap = map; 180 } 181 } 182 ; 183 } 184 ; 185 186 @Before setUp()187 public void setUp() { 188 MockitoAnnotations.initMocks(this); 189 mSharedPreferences = new FakeSharedPreferences(); 190 doReturn(mSharedPreferences) 191 .when(mContext) 192 .getSharedPreferences(eq(ONS_ATOM_LOG_FILE), eq(Context.MODE_PRIVATE)); 193 doReturn(123).when(mSubInfoId1).getCarrierId(); 194 doReturn(456).when(mSubInfoId2).getCarrierId(); 195 doReturn(mSubInfoId1).when(mSubscriptionManager).getActiveSubscriptionInfo(1); 196 doReturn(mSubInfoId2).when(mSubscriptionManager).getActiveSubscriptionInfo(2); 197 mONSStats = new ONSStats(mContext, mSubscriptionManager); 198 } 199 200 @After tearDown()201 public void tearDown() { 202 mSharedPreferences.edit().clear().apply(); 203 } 204 205 @Test testLogEvent()206 public void testLogEvent() { 207 ONSStatsInfo info = 208 new ONSStatsInfo.Builder() 209 .setPrimarySimSubId(1) 210 .setProvisioningResult(mContext, Result.ERR_CANNOT_SWITCH_TO_DUAL_SIM_MODE) 211 .build(); 212 assertTrue(mONSStats.logEvent(info)); 213 } 214 215 @Test testIgnoredLogEvents()216 public void testIgnoredLogEvents() { 217 // ignored error codes should not log. 218 ONSStatsInfo info = 219 new ONSStatsInfo.Builder() 220 .setProvisioningResult(mContext, Result.DOWNLOAD_REQUESTED) 221 .build(); 222 assertFalse(mONSStats.logEvent(info)); 223 224 info = 225 new ONSStatsInfo.Builder() 226 .setProvisioningResult(mContext, Result.ERR_NO_SIM_INSERTED) 227 .build(); 228 assertFalse(mONSStats.logEvent(info)); 229 230 info = 231 new ONSStatsInfo.Builder() 232 .setProvisioningResult(mContext, Result.ERR_DUPLICATE_DOWNLOAD_REQUEST) 233 .build(); 234 assertFalse(mONSStats.logEvent(info)); 235 236 info = 237 new ONSStatsInfo.Builder() 238 .setProvisioningResult(mContext, Result.ERR_SWITCHING_TO_DUAL_SIM_MODE) 239 .build(); 240 assertFalse(mONSStats.logEvent(info)); 241 } 242 243 @Test testRepeatedLogEvents()244 public void testRepeatedLogEvents() { 245 ONSStatsInfo info; 246 info = 247 new ONSStatsInfo.Builder() 248 .setDownloadResult(DownloadRetryResultCode.ERR_MEMORY_FULL) 249 .setDetailedErrCode(10011) 250 .build(); 251 assertTrue(mONSStats.logEvent(info)); 252 253 // same result should not log consecutively 254 assertFalse(mONSStats.logEvent(info)); 255 assertFalse(mONSStats.logEvent(info)); 256 } 257 258 @Test testRepeatedAllowedLogEvents()259 public void testRepeatedAllowedLogEvents() { 260 ONSStatsInfo info; 261 info = 262 new ONSStatsInfo.Builder() 263 .setProvisioningResult(mContext, Result.ERR_DOWNLOADED_ESIM_NOT_FOUND) 264 .build(); 265 assertTrue(mONSStats.logEvent(info)); 266 267 // ERR_DOWNLOADED_ESIM_NOT_FOUND is allowed to log consecutively 268 assertTrue(mONSStats.logEvent(info)); 269 assertTrue(mONSStats.logEvent(info)); 270 271 info = 272 new ONSStatsInfo.Builder() 273 .setDownloadResult(DownloadRetryResultCode.ERR_INSTALL_ESIM_PROFILE_FAILED) 274 .build(); 275 assertTrue(mONSStats.logEvent(info)); 276 277 // ERR_INSTALL_ESIM_PROFILE_FAILED is allowed to log consecutively 278 assertTrue(mONSStats.logEvent(info)); 279 assertTrue(mONSStats.logEvent(info)); 280 } 281 282 @Test testRepeatedSuccessLogEvents()283 public void testRepeatedSuccessLogEvents() { 284 ONSStatsInfo info; 285 info = 286 new ONSStatsInfo.Builder() 287 .setProvisioningResult(mContext, Result.SUCCESS) 288 .setRetryCount(2) 289 .build(); 290 291 // should log every time if eSIM is newly downloaded. 292 assertTrue(mONSStats.logEvent(info)); 293 assertTrue(mONSStats.logEvent(info)); 294 295 info = new ONSStatsInfo.Builder().setProvisioningResult(mContext, Result.SUCCESS).build(); 296 // should log even if eSIM is already downloaded and event triggered just to group it. 297 assertTrue(mONSStats.logEvent(info)); 298 assertTrue(mONSStats.logEvent(info)); 299 } 300 301 @Test testRepeatedErrorWithInfoChangeLogEvents()302 public void testRepeatedErrorWithInfoChangeLogEvents() { 303 ONSStatsInfo info = 304 new ONSStatsInfo.Builder() 305 .setPrimarySimSubId(1) 306 .setProvisioningResult(mContext, Result.ERR_AUTO_PROVISIONING_DISABLED) 307 .build(); 308 assertTrue(mONSStats.logEvent(info)); 309 310 // Same error should log if the info is changed. 311 info = new ONSStatsInfo.Builder(info).setPrimarySimSubId(2).build(); 312 assertTrue(mONSStats.logEvent(info)); 313 314 // no change in info 315 assertFalse(mONSStats.logEvent(info)); 316 } 317 318 @Test testDetailedErrorCodeLogEvents()319 public void testDetailedErrorCodeLogEvents() { 320 ONSStatsInfo info; 321 info = new ONSStatsInfo.Builder() 322 .setProvisioningResult(mContext, Result.ERR_WAITING_FOR_INTERNET_CONNECTION) 323 .build(); 324 assertTrue(mONSStats.logEvent(info)); 325 326 // For provisioning errors; Result enum ordinal is set as detailed error code. 327 assertEquals( 328 Result.ERR_WAITING_FOR_INTERNET_CONNECTION.ordinal(), 329 mSharedPreferences.getInt(KEY_DETAILED_ERROR_CODE, -1)); 330 assertEquals( 331 Result.ERR_WAITING_FOR_INTERNET_CONNECTION.ordinal(), info.detailedErrCode()); 332 333 // For Download errors; detailed error code is updated from EuiccManager. 334 info = 335 new ONSStatsInfo.Builder(info) 336 .setDownloadResult(DownloadRetryResultCode.ERR_MEMORY_FULL) 337 .setDetailedErrCode(10223) 338 .build(); 339 assertTrue(mONSStats.logEvent(info)); 340 assertEquals(10223, mSharedPreferences.getInt(KEY_DETAILED_ERROR_CODE, -1)); 341 assertEquals(10223, info.detailedErrCode()); 342 } 343 } 344