/* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.ons; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import android.content.Context; import android.content.SharedPreferences; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import com.android.ons.ONSProfileActivator.Result; import com.android.ons.ONSProfileDownloader.DownloadRetryResultCode; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.Spy; import java.util.HashMap; import java.util.Map; import java.util.Set; @RunWith(JUnit4.class) public class ONSStatsTest { private static final String ONS_ATOM_LOG_FILE = "ons_atom_log_info"; private static final String KEY_DETAILED_ERROR_CODE = "_detailed_error_code"; @Spy private Context mContext; @Mock private SubscriptionManager mSubscriptionManager; private SharedPreferences mSharedPreferences; @Mock private SubscriptionInfo mSubInfoId1; @Mock private SubscriptionInfo mSubInfoId2; private ONSStats mONSStats; private class FakeSharedPreferences implements SharedPreferences { HashMap mMap = new HashMap<>(); @Override public Map getAll() { return mMap; } @Override public String getString(String key, String defValue) { return (String) mMap.getOrDefault(key, defValue); } @Override public Set getStringSet(String key, Set defValues) { if (mMap.containsKey(key)) { return (Set) mMap.get(key); } return defValues; } @Override public int getInt(String key, int defValue) { return (int) mMap.getOrDefault(key, defValue); } @Override public long getLong(String key, long defValue) { return 0; // not used } @Override public float getFloat(String key, float defValue) { return (float) mMap.getOrDefault(key, defValue); } @Override public boolean getBoolean(String key, boolean defValue) { return (boolean) mMap.getOrDefault(key, defValue); } @Override public boolean contains(String key) { return mMap.containsKey(key); } @Override public Editor edit() { TestEditor editor = new TestEditor(); editor.map = mMap; return editor; } @Override public void registerOnSharedPreferenceChangeListener( OnSharedPreferenceChangeListener listener) {} @Override public void unregisterOnSharedPreferenceChangeListener( OnSharedPreferenceChangeListener listener) {} private class TestEditor implements SharedPreferences.Editor { HashMap map = new HashMap<>(); @Override public SharedPreferences.Editor putString(String key, String value) { map.put(key, value); return this; } @Override public SharedPreferences.Editor putStringSet(String key, Set values) { map.put(key, values); return this; } @Override public SharedPreferences.Editor putInt(String key, int value) { map.put(key, value); return this; } @Override public SharedPreferences.Editor putLong(String key, long value) { map.put(key, value); return this; } @Override public SharedPreferences.Editor putFloat(String key, float value) { map.put(key, value); return this; } @Override public SharedPreferences.Editor putBoolean(String key, boolean value) { map.put(key, value); return this; } @Override public SharedPreferences.Editor remove(String key) { map.remove(key); return this; } @Override public SharedPreferences.Editor clear() { map.clear(); return this; } @Override public boolean commit() { mMap = map; return true; } @Override public void apply() { mMap = map; } } ; } ; @Before public void setUp() { MockitoAnnotations.initMocks(this); mSharedPreferences = new FakeSharedPreferences(); doReturn(mSharedPreferences) .when(mContext) .getSharedPreferences(eq(ONS_ATOM_LOG_FILE), eq(Context.MODE_PRIVATE)); doReturn(123).when(mSubInfoId1).getCarrierId(); doReturn(456).when(mSubInfoId2).getCarrierId(); doReturn(mSubInfoId1).when(mSubscriptionManager).getActiveSubscriptionInfo(1); doReturn(mSubInfoId2).when(mSubscriptionManager).getActiveSubscriptionInfo(2); mONSStats = new ONSStats(mContext, mSubscriptionManager); } @After public void tearDown() { mSharedPreferences.edit().clear().apply(); } @Test public void testLogEvent() { ONSStatsInfo info = new ONSStatsInfo.Builder() .setPrimarySimSubId(1) .setProvisioningResult(mContext, Result.ERR_CANNOT_SWITCH_TO_DUAL_SIM_MODE) .build(); assertTrue(mONSStats.logEvent(info)); } @Test public void testIgnoredLogEvents() { // ignored error codes should not log. ONSStatsInfo info = new ONSStatsInfo.Builder() .setProvisioningResult(mContext, Result.DOWNLOAD_REQUESTED) .build(); assertFalse(mONSStats.logEvent(info)); info = new ONSStatsInfo.Builder() .setProvisioningResult(mContext, Result.ERR_NO_SIM_INSERTED) .build(); assertFalse(mONSStats.logEvent(info)); info = new ONSStatsInfo.Builder() .setProvisioningResult(mContext, Result.ERR_DUPLICATE_DOWNLOAD_REQUEST) .build(); assertFalse(mONSStats.logEvent(info)); info = new ONSStatsInfo.Builder() .setProvisioningResult(mContext, Result.ERR_SWITCHING_TO_DUAL_SIM_MODE) .build(); assertFalse(mONSStats.logEvent(info)); } @Test public void testRepeatedLogEvents() { ONSStatsInfo info; info = new ONSStatsInfo.Builder() .setDownloadResult(DownloadRetryResultCode.ERR_MEMORY_FULL) .setDetailedErrCode(10011) .build(); assertTrue(mONSStats.logEvent(info)); // same result should not log consecutively assertFalse(mONSStats.logEvent(info)); assertFalse(mONSStats.logEvent(info)); } @Test public void testRepeatedAllowedLogEvents() { ONSStatsInfo info; info = new ONSStatsInfo.Builder() .setProvisioningResult(mContext, Result.ERR_DOWNLOADED_ESIM_NOT_FOUND) .build(); assertTrue(mONSStats.logEvent(info)); // ERR_DOWNLOADED_ESIM_NOT_FOUND is allowed to log consecutively assertTrue(mONSStats.logEvent(info)); assertTrue(mONSStats.logEvent(info)); info = new ONSStatsInfo.Builder() .setDownloadResult(DownloadRetryResultCode.ERR_INSTALL_ESIM_PROFILE_FAILED) .build(); assertTrue(mONSStats.logEvent(info)); // ERR_INSTALL_ESIM_PROFILE_FAILED is allowed to log consecutively assertTrue(mONSStats.logEvent(info)); assertTrue(mONSStats.logEvent(info)); } @Test public void testRepeatedSuccessLogEvents() { ONSStatsInfo info; info = new ONSStatsInfo.Builder() .setProvisioningResult(mContext, Result.SUCCESS) .setRetryCount(2) .build(); // should log every time if eSIM is newly downloaded. assertTrue(mONSStats.logEvent(info)); assertTrue(mONSStats.logEvent(info)); info = new ONSStatsInfo.Builder().setProvisioningResult(mContext, Result.SUCCESS).build(); // should log even if eSIM is already downloaded and event triggered just to group it. assertTrue(mONSStats.logEvent(info)); assertTrue(mONSStats.logEvent(info)); } @Test public void testRepeatedErrorWithInfoChangeLogEvents() { ONSStatsInfo info = new ONSStatsInfo.Builder() .setPrimarySimSubId(1) .setProvisioningResult(mContext, Result.ERR_AUTO_PROVISIONING_DISABLED) .build(); assertTrue(mONSStats.logEvent(info)); // Same error should log if the info is changed. info = new ONSStatsInfo.Builder(info).setPrimarySimSubId(2).build(); assertTrue(mONSStats.logEvent(info)); // no change in info assertFalse(mONSStats.logEvent(info)); } @Test public void testDetailedErrorCodeLogEvents() { ONSStatsInfo info; info = new ONSStatsInfo.Builder() .setProvisioningResult(mContext, Result.ERR_WAITING_FOR_INTERNET_CONNECTION) .build(); assertTrue(mONSStats.logEvent(info)); // For provisioning errors; Result enum ordinal is set as detailed error code. assertEquals( Result.ERR_WAITING_FOR_INTERNET_CONNECTION.ordinal(), mSharedPreferences.getInt(KEY_DETAILED_ERROR_CODE, -1)); assertEquals( Result.ERR_WAITING_FOR_INTERNET_CONNECTION.ordinal(), info.detailedErrCode()); // For Download errors; detailed error code is updated from EuiccManager. info = new ONSStatsInfo.Builder(info) .setDownloadResult(DownloadRetryResultCode.ERR_MEMORY_FULL) .setDetailedErrCode(10223) .build(); assertTrue(mONSStats.logEvent(info)); assertEquals(10223, mSharedPreferences.getInt(KEY_DETAILED_ERROR_CODE, -1)); assertEquals(10223, info.detailedErrCode()); } }