diff options
author | android-build-team Robot <android-build-team-robot@google.com> | 2021-06-10 01:09:54 +0000 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2021-06-10 01:09:54 +0000 |
commit | a321c3b61e7b5ca9ae5a9ae1c701585841633b1e (patch) | |
tree | a7a05b4073b9f4cf1495803ddd8c2f3f05b8c425 | |
parent | c00e143d14dfbb3ef446b4591fcec473099ee3ad (diff) | |
parent | a014eea499ce47dd6e5f3ee19cc68fab3ea3a5ca (diff) | |
download | ImsServiceEntitlement-a321c3b61e7b5ca9ae5a9ae1c701585841633b1e.tar.gz |
Snap for 7442307 from a014eea499ce47dd6e5f3ee19cc68fab3ea3a5ca to sc-d2-release
Change-Id: I902c130dc8eb68587cbf9ffda599be0a7ee29832
12 files changed, 522 insertions, 41 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml index fa64f24..2311c9b 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -64,6 +64,14 @@ android:permission="android.permission.BIND_JOB_SERVICE"> </service> <!-- END: FCM related components --> + + <receiver + android:name=".ImsEntitlementReceiver" + android:exported="true"> + <intent-filter> + <action android:name="android.telephony.action.CARRIER_CONFIG_CHANGED" /> + </intent-filter> + </receiver> </application> </manifest> diff --git a/src/com/android/imsserviceentitlement/EntitlementUtils.java b/src/com/android/imsserviceentitlement/EntitlementUtils.java index 62943c7..83dab5a 100644 --- a/src/com/android/imsserviceentitlement/EntitlementUtils.java +++ b/src/com/android/imsserviceentitlement/EntitlementUtils.java @@ -16,8 +16,12 @@ package com.android.imsserviceentitlement; +import static com.android.imsserviceentitlement.utils.Executors.getAsyncExecutor; +import static com.android.imsserviceentitlement.utils.Executors.getDirectExecutor; + import android.util.Log; +import androidx.annotation.MainThread; import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; @@ -27,39 +31,25 @@ import com.android.imsserviceentitlement.entitlement.EntitlementResult; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.MoreExecutors; - -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -/** Handle entitlement check */ -public class EntitlementUtils { +/** Handles entitlement check from main thread. */ +public final class EntitlementUtils { public static final String LOG_TAG = "IMSSE-EntitlementUtils"; - private static final ExecutorService EXECUTOR_SERVICE = Executors.newCachedThreadPool(); - private static final ExecutorService DIRECT_EXECUTOR_SERVICE = - MoreExecutors.newDirectExecutorService(); private static ListenableFuture<EntitlementResult> sCheckEntitlementFuture; - /** - * Whether to execute entitlementCheck in caller's thread, set to true via reflection for test. - */ - private static boolean sUseDirectExecutorForTest = false; - private EntitlementUtils() {} /** * Performs the entitlement status check, and passes the result via {@link * EntitlementResultCallback}. */ + @MainThread public static void entitlementCheck( ImsEntitlementApi activationApi, EntitlementResultCallback callback) { - ListeningExecutorService service = - MoreExecutors.listeningDecorator( - sUseDirectExecutorForTest ? DIRECT_EXECUTOR_SERVICE : EXECUTOR_SERVICE); - sCheckEntitlementFuture = service.submit(() -> getEntitlementStatus(activationApi)); + sCheckEntitlementFuture = + Futures.submit(() -> getEntitlementStatus(activationApi), getAsyncExecutor()); Futures.addCallback( sCheckEntitlementFuture, new FutureCallback<EntitlementResult>() { @@ -75,7 +65,7 @@ public class EntitlementUtils { sCheckEntitlementFuture = null; } }, - DIRECT_EXECUTOR_SERVICE); + getDirectExecutor()); } /** Cancels the running task of entitlement status check if exist. */ diff --git a/src/com/android/imsserviceentitlement/ImsEntitlementPollingService.java b/src/com/android/imsserviceentitlement/ImsEntitlementPollingService.java index d5c834b..f35503b 100644 --- a/src/com/android/imsserviceentitlement/ImsEntitlementPollingService.java +++ b/src/com/android/imsserviceentitlement/ImsEntitlementPollingService.java @@ -48,9 +48,9 @@ import com.android.imsserviceentitlement.utils.ImsUtils; import com.android.imsserviceentitlement.utils.TelephonyUtils; /** - * The {@link JobService} for querying entitlement status in the background. - * The jobId is unique for different subId + job combination, so can run the same job for different - * subIds w/o cancelling each others. See {@link JobManager}. + * The {@link JobService} for querying entitlement status in the background. The jobId is unique for + * different subId + job combination, so can run the same job for different subIds w/o cancelling + * each others. See {@link JobManager}. */ public class ImsEntitlementPollingService extends JobService { private static final String TAG = "IMSSE-ImsEntitlementPollingService"; @@ -250,8 +250,8 @@ public class ImsEntitlementPollingService extends JobService { } /** - * Returns {@code true} when {@code EntitlementResult} says WFC is not activated; - * Otherwise {@code false} if {@code EntitlementResult} is not of any known pattern. + * Returns {@code true} when {@code EntitlementResult} says WFC is not activated; Otherwise + * {@code false} if {@code EntitlementResult} is not of any known pattern. */ private boolean shouldTurnOffWfc(@Nullable EntitlementResult result) { if (result == null) { diff --git a/src/com/android/imsserviceentitlement/ImsEntitlementReceiver.java b/src/com/android/imsserviceentitlement/ImsEntitlementReceiver.java new file mode 100644 index 0000000..dc78b0a --- /dev/null +++ b/src/com/android/imsserviceentitlement/ImsEntitlementReceiver.java @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2021 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.imsserviceentitlement; + +import static com.android.imsserviceentitlement.entitlement.EntitlementConfiguration.ClientBehavior.NEEDS_TO_RESET; +import static com.android.imsserviceentitlement.entitlement.EntitlementConfiguration.ClientBehavior.VALID_DURING_VALIDITY; +import static com.android.imsserviceentitlement.entitlement.EntitlementConfiguration.ClientBehavior.VALID_WITHOUT_DURATION; +import static com.android.imsserviceentitlement.utils.Executors.getAsyncExecutor; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.UserManager; +import android.provider.Settings; +import android.telephony.CarrierConfigManager; +import android.telephony.SubscriptionManager; +import android.util.Log; + +import androidx.annotation.VisibleForTesting; +import androidx.annotation.WorkerThread; + +import com.android.imsserviceentitlement.entitlement.EntitlementConfiguration; +import com.android.imsserviceentitlement.entitlement.EntitlementConfiguration.ClientBehavior; +import com.android.imsserviceentitlement.job.JobManager; +import com.android.imsserviceentitlement.utils.TelephonyUtils; + +/** Watches events and manages service entitlement polling. */ +public class ImsEntitlementReceiver extends BroadcastReceiver { + private static final String TAG = "IMSSE-ImsEntitlementReceiver"; + + /** + * Shared preference name for activation information, the key used in this file should append + * slot id if the value depended on carrier. + */ + private static final String PREFERENCE_ACTIVATION_INFO = "PREFERENCE_ACTIVATION_INFO"; + /** + * Shared preference key for last known subscription id of a SIM slot; default value {@link + * SubscriptionManager#INVALID_SUBSCRIPTION_ID}. + */ + private static final String KEY_LAST_SUB_ID = "last_sub_id_"; + /** Shared preference key for last boot count. */ + private static final String KEY_LAST_BOOT_COUNT = "last_boot_count_"; + + @Override + public void onReceive(Context context, Intent intent) { + int currentSubId = + intent.getIntExtra( + SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, + SubscriptionManager.INVALID_SUBSCRIPTION_ID); + int slotId = + intent.getIntExtra( + SubscriptionManager.EXTRA_SLOT_INDEX, + SubscriptionManager.INVALID_SIM_SLOT_INDEX); + Dependencies dependencies = createDependency(context, currentSubId); + if (!dependencies.userManager.isSystemUser() + || !TelephonyUtils.isImsProvisioningRequired(context, currentSubId)) { + return; + } + + String action = intent.getAction(); + if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action)) { + final PendingResult result = goAsync(); + getAsyncExecutor().execute( + () -> handleCarrierConfigChanged( + context, currentSubId, slotId, dependencies.jobManager, result)); + } + } + + /** + * Handles the event of SIM change and device boot up while receiving {@link + * CarrierConfigManager#ACTION_CARRIER_CONFIG_CHANGED}. + */ + @WorkerThread + private void handleCarrierConfigChanged( + Context context, int currentSubId, int slotId, JobManager jobManager, + PendingResult result) { + if (!SubscriptionManager.isValidSubscriptionId(currentSubId)) { + return; + } + boolean shouldQuery = false; + + // Handle device boot up. + if (isBootUp(context, slotId)) { + ClientBehavior clientBehavior = + new EntitlementConfiguration(context, currentSubId).entitlementValidation(); + Log.d(TAG, "Device boot up, clientBehavior=" + clientBehavior); + if (clientBehavior == VALID_DURING_VALIDITY + || clientBehavior == VALID_WITHOUT_DURATION + || clientBehavior == NEEDS_TO_RESET) { + shouldQuery = true; + } + } + + // Handle SIM changed. + int lastSubId = getAndSetSubId(context, currentSubId, slotId); + if (currentSubId != lastSubId) { + Log.d(TAG, + "SubId for slot " + slotId + " changed: " + lastSubId + " -> " + currentSubId); + if (SubscriptionManager.isValidSubscriptionId(lastSubId)) { + new EntitlementConfiguration(context, lastSubId).reset(); + } + shouldQuery = true; + } + + if (shouldQuery) { + jobManager.queryEntitlementStatusOnceNetworkReady(); + } + + if (result != null) { + result.finish(); + } + } + + /** + * Returns {@code true} if current boot count greater than previous one. Saves the latest boot + * count into shared preference. + */ + @VisibleForTesting + boolean isBootUp(Context context, int slotId) { + SharedPreferences preferences = + context.getSharedPreferences(PREFERENCE_ACTIVATION_INFO, Context.MODE_PRIVATE); + int lastBootCount = preferences.getInt(KEY_LAST_BOOT_COUNT + slotId, 0); + int currentBootCount = + Settings.Global.getInt( + context.getContentResolver(), Settings.Global.BOOT_COUNT, /* def= */ -1); + preferences.edit().putInt(KEY_LAST_BOOT_COUNT + slotId, currentBootCount).apply(); + + return currentBootCount != lastBootCount; + } + + private int getAndSetSubId(Context context, int currentSubId, int slotId) { + SharedPreferences preferences = + context.getSharedPreferences(PREFERENCE_ACTIVATION_INFO, Context.MODE_PRIVATE); + int lastSubId = preferences.getInt( + KEY_LAST_SUB_ID + slotId, SubscriptionManager.INVALID_SUBSCRIPTION_ID); + preferences.edit().putInt(KEY_LAST_SUB_ID + slotId, currentSubId).apply(); + return lastSubId; + } + + /** Returns initialized dependencies */ + @VisibleForTesting + Dependencies createDependency(Context context, int subId) { + // Wrap return value + Dependencies ret = new Dependencies(); + ret.telephonyUtils = new TelephonyUtils(context, subId); + ret.userManager = context.getSystemService(UserManager.class); + ret.jobManager = + JobManager.getInstance(context, ImsEntitlementPollingService.COMPONENT_NAME, subId); + return ret; + } + + /** A collection of dependency objects */ + protected static class Dependencies { + public TelephonyUtils telephonyUtils; + public UserManager userManager; + public JobManager jobManager; + } +} diff --git a/src/com/android/imsserviceentitlement/WfcActivationController.java b/src/com/android/imsserviceentitlement/WfcActivationController.java index bb25278..ed63bf9 100644 --- a/src/com/android/imsserviceentitlement/WfcActivationController.java +++ b/src/com/android/imsserviceentitlement/WfcActivationController.java @@ -60,8 +60,6 @@ public class WfcActivationController { private static final int ENTITLEMENT_STATUS_UPDATE_RETRY_MAX = 6; private static final long ENTITLEMENT_STATUS_UPDATE_RETRY_INTERVAL_MS = Duration.ofSeconds(5).toMillis(); - private static final long ENTITLEMENT_STATUS_UPDATE_RETRY_INTERVAL_MS_ATT = - Duration.ofMinutes(30).toMillis(); // Dependencies private final WfcActivationUi mActivationUi; diff --git a/src/com/android/imsserviceentitlement/entitlement/EntitlementConfiguration.java b/src/com/android/imsserviceentitlement/entitlement/EntitlementConfiguration.java index 8b51988..7617cc6 100644 --- a/src/com/android/imsserviceentitlement/entitlement/EntitlementConfiguration.java +++ b/src/com/android/imsserviceentitlement/entitlement/EntitlementConfiguration.java @@ -35,23 +35,23 @@ public class EntitlementConfiguration { /** Default value of VoLTE/VoWifi/SMSoverIP entitlemenet status. */ private static final int INCOMPATIBLE_STATE = 2; - private final EntitlementConfigurationsDataStore mConfigutationsDataStore; + private final EntitlementConfigurationsDataStore mConfigurationsDataStore; private XmlDoc mXmlDoc = new XmlDoc(null); public EntitlementConfiguration(Context context, int subId) { - mConfigutationsDataStore = new EntitlementConfigurationsDataStore(context, subId); - mConfigutationsDataStore.get().ifPresent(rawXml -> mXmlDoc = new XmlDoc(rawXml)); + mConfigurationsDataStore = EntitlementConfigurationsDataStore.getInstance(context, subId); + mConfigurationsDataStore.get().ifPresent(rawXml -> mXmlDoc = new XmlDoc(rawXml)); } /** Update VERS characteristics with given version and validity. */ public void update(String rawXml) { - mConfigutationsDataStore.set(rawXml); + mConfigurationsDataStore.set(rawXml); mXmlDoc = new XmlDoc(rawXml); } /** - * Returns VoLTE entitlement status from the {@link EntitlementCharacteristicDataStore}. If no + * Returns VoLTE entitlement status from the {@link EntitlementConfigurationsDataStore}. If no * data exist then return the default value {@link #INCOMPATIBLE_STATE}. */ public int getVolteStatus() { @@ -64,7 +64,7 @@ public class EntitlementConfiguration { } /** - * Returns VoWiFi entitlement status from the {@link EntitlementCharacteristicDataStore}. If no + * Returns VoWiFi entitlement status from the {@link EntitlementConfigurationsDataStore}. If no * data exist then return the default value {@link #INCOMPATIBLE_STATE}. */ public int getVoWifiStatus() { @@ -77,7 +77,7 @@ public class EntitlementConfiguration { } /** - * Returns SMSoIP entitlement status from the {@link EntitlementCharacteristicDataStore}. If no + * Returns SMSoIP entitlement status from the {@link EntitlementConfigurationsDataStore}. If no * data exist then return the default value {@link #INCOMPATIBLE_STATE}. */ public int getSmsOverIpStatus() { @@ -90,7 +90,7 @@ public class EntitlementConfiguration { } /** - * Returns token stored in the {@link EntitlementCharacteristicDataStore} if it is in validity + * Returns token stored in the {@link EntitlementConfigurationsDataStore} if it is in validity * period. Returns {@link Optional#empty()} if the token was expired or the value of token * validity not positive. */ @@ -101,7 +101,7 @@ public class EntitlementConfiguration { } private boolean isTokenInValidityPeriod() { - long queryTimeMillis = mConfigutationsDataStore.getQueryTimeMillis(); + long queryTimeMillis = mConfigurationsDataStore.getQueryTimeMillis(); long tokenValidityMillis = TimeUnit.SECONDS.toMillis(getTokenValidity()); if (queryTimeMillis <= 0) { @@ -115,7 +115,7 @@ public class EntitlementConfiguration { return true; } - return System.currentTimeMillis() - queryTimeMillis < tokenValidityMillis; + return (System.currentTimeMillis() - queryTimeMillis) < tokenValidityMillis; } /** diff --git a/src/com/android/imsserviceentitlement/entitlement/EntitlementConfigurationsDataStore.java b/src/com/android/imsserviceentitlement/entitlement/EntitlementConfigurationsDataStore.java index 29ea637..6947e2b 100644 --- a/src/com/android/imsserviceentitlement/entitlement/EntitlementConfigurationsDataStore.java +++ b/src/com/android/imsserviceentitlement/entitlement/EntitlementConfigurationsDataStore.java @@ -18,6 +18,7 @@ package com.android.imsserviceentitlement.entitlement; import android.content.Context; import android.content.SharedPreferences; +import android.util.SparseArray; import java.util.Optional; @@ -29,7 +30,17 @@ class EntitlementConfigurationsDataStore { private final SharedPreferences mPreferences; - EntitlementConfigurationsDataStore(Context context, int subId) { + private static final SparseArray<EntitlementConfigurationsDataStore> sInstances = + new SparseArray<>(); + + public static EntitlementConfigurationsDataStore getInstance(Context context, int subId) { + if (sInstances.get(subId) == null) { + sInstances.put(subId, new EntitlementConfigurationsDataStore(context, subId)); + } + return sInstances.get(subId); + } + + private EntitlementConfigurationsDataStore(Context context, int subId) { this.mPreferences = context.getSharedPreferences( PREFERENCE_ENTITLEMENT_CHARACTERISTICS + "_" + subId, Context.MODE_PRIVATE); diff --git a/src/com/android/imsserviceentitlement/utils/Executors.java b/src/com/android/imsserviceentitlement/utils/Executors.java new file mode 100644 index 0000000..3f8e68f --- /dev/null +++ b/src/com/android/imsserviceentitlement/utils/Executors.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2021 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.imsserviceentitlement.utils; + +import static android.os.AsyncTask.THREAD_POOL_EXECUTOR; + +import java.util.concurrent.Executor; + +/** Provides executors for running the tasks asynchronized. */ +public final class Executors { + /** + * Whether to execute entitlementCheck in caller's thread, set to true via reflection for test. + */ + private static boolean sUseDirectExecutorForTest = false; + + private static final Executor ASYNC_EXECUTOR = THREAD_POOL_EXECUTOR; + private static final Executor DIRECT_EXECUTOR = Runnable::run; + + private Executors() {} + + /** Returns {@link Executor} executing tasks asynchronously. */ + public static Executor getAsyncExecutor() { + return sUseDirectExecutorForTest ? DIRECT_EXECUTOR : ASYNC_EXECUTOR; + } + + /** Returns {@link Executor} executing tasks from the caller's thread. */ + public static Executor getDirectExecutor() { + return DIRECT_EXECUTOR; + } +} diff --git a/tests/unittests/src/com/android/imsserviceentitlement/EntitlementUtilsTest.java b/tests/unittests/src/com/android/imsserviceentitlement/EntitlementUtilsTest.java index 928273d..474e755 100644 --- a/tests/unittests/src/com/android/imsserviceentitlement/EntitlementUtilsTest.java +++ b/tests/unittests/src/com/android/imsserviceentitlement/EntitlementUtilsTest.java @@ -24,6 +24,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.imsserviceentitlement.WfcActivationController.EntitlementResultCallback; import com.android.imsserviceentitlement.entitlement.EntitlementResult; +import com.android.imsserviceentitlement.utils.Executors; import org.junit.Before; import org.junit.Rule; @@ -44,7 +45,7 @@ public class EntitlementUtilsTest { @Before public void setup() throws Exception { - Field field = EntitlementUtils.class.getDeclaredField("sUseDirectExecutorForTest"); + Field field = Executors.class.getDeclaredField("sUseDirectExecutorForTest"); field.setAccessible(true); field.set(null, true); } diff --git a/tests/unittests/src/com/android/imsserviceentitlement/ImsEntitlementPollingServiceTest.java b/tests/unittests/src/com/android/imsserviceentitlement/ImsEntitlementPollingServiceTest.java index 6dc333c..2737c62 100644 --- a/tests/unittests/src/com/android/imsserviceentitlement/ImsEntitlementPollingServiceTest.java +++ b/tests/unittests/src/com/android/imsserviceentitlement/ImsEntitlementPollingServiceTest.java @@ -16,11 +16,14 @@ package com.android.imsserviceentitlement; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.job.JobParameters; +import android.app.job.JobScheduler; import android.content.Context; import android.os.PersistableBundle; import android.telephony.CarrierConfigManager; @@ -67,17 +70,19 @@ public class ImsEntitlementPollingServiceTest { @Mock private CarrierConfigManager mCarrierConfigManager; private ImsEntitlementPollingService mService; + private JobScheduler mScheduler; private static final int SUB_ID = 1; private static final int SLOT_ID = 0; @Before - public void setup() throws Exception { + public void setUp() throws Exception { mService = new ImsEntitlementPollingService(); mService.attachBaseContext(mContext); mService.onCreate(); mService.onBind(null); mService.injectImsEntitlementApi(mImsEntitlementApi); + mScheduler = mContext.getSystemService(JobScheduler.class); setActivedSubscription(); setupImsUtils(); setJobParameters(); @@ -154,6 +159,16 @@ public class ImsEntitlementPollingServiceTest { verify(mImsUtils).setSmsoipProvisioned(true); } + @Test + public void enqueueJob_hasJob() { + ImsEntitlementPollingService.enqueueJob(mContext, SUB_ID, 0); + + assertThat( + mScheduler.getPendingJob( + jobIdWithSubId(JobManager.QUERY_ENTITLEMENT_STATUS_JOB_ID, SUB_ID))) + .isNotNull(); + } + private void setActivedSubscription() { when(mSubscriptionInfo.getSimSlotIndex()).thenReturn(SLOT_ID); when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(mSubscriptionInfo); @@ -209,6 +224,10 @@ public class ImsEntitlementPollingServiceTest { .build(); } + private int jobIdWithSubId(int jobId, int subId) { + return 1000 * subId + jobId; + } + private static final Ts43VowifiStatus sDisableVoWiFi = Ts43VowifiStatus.builder() .setEntitlementStatus(EntitlementStatus.DISABLED) diff --git a/tests/unittests/src/com/android/imsserviceentitlement/ImsEntitlementReceiverTest.java b/tests/unittests/src/com/android/imsserviceentitlement/ImsEntitlementReceiverTest.java new file mode 100644 index 0000000..dbea9a1 --- /dev/null +++ b/tests/unittests/src/com/android/imsserviceentitlement/ImsEntitlementReceiverTest.java @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2021 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.imsserviceentitlement; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.PersistableBundle; +import android.os.UserManager; +import android.telephony.CarrierConfigManager; +import android.telephony.SubscriptionManager; + +import androidx.test.core.app.ApplicationProvider; +import androidx.test.runner.AndroidJUnit4; + +import com.android.imsserviceentitlement.entitlement.EntitlementConfiguration; +import com.android.imsserviceentitlement.job.JobManager; +import com.android.imsserviceentitlement.utils.Executors; +import com.android.imsserviceentitlement.utils.TelephonyUtils; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.lang.reflect.Field; + +@RunWith(AndroidJUnit4.class) +public class ImsEntitlementReceiverTest { + private static final int SUB_ID = 1; + private static final int LAST_SUB_ID = 2; + private static final String RAW_XML = + "<wap-provisioningdoc version=\"1.1\">\n" + + " <characteristic type=\"APPLICATION\">\n" + + " <parm name=\"AppID\" value=\"ap2004\"/>\n" + + " <parm name=\"EntitlementStatus\" value=\"1\"/>\n" + + " </characteristic>\n" + + "</wap-provisioningdoc>\n"; + + private static final String RAW_XML_VERSION_0_VALIDITY_0 = + "<wap-provisioningdoc version=\"1.1\">\n" + + " <characteristic type=\"VERS\">\n" + + " <parm name=\"version\" value=\"0\"/>\n" + + " <parm name=\"validity\" value=\"0\"/>\n" + + " </characteristic>\n" + + "</wap-provisioningdoc>\n"; + + private static final String RAW_XML_INVALID_VERS = + "<wap-provisioningdoc version=\"1.1\">\n" + + " <characteristic type=\"VERS\">\n" + + " <parm name=\"version\" value=\"-1\"/>\n" + + " <parm name=\"validity\" value=\"-1\"/>\n" + + " </characteristic>\n" + + "</wap-provisioningdoc>\n"; + private static final ComponentName POLLING_SERVICE_COMPONENT_NAME = + ComponentName.unflattenFromString( + "com.android.imsserviceentitlement/.ImsEntitlementPollingService"); + + @Rule public final MockitoRule rule = MockitoJUnit.rule(); + + @Mock private TelephonyUtils mMockTelephonyUtils; + @Mock private UserManager mMockUserManager; + @Mock private CarrierConfigManager mCarrierConfigManager; + @Mock private JobManager mMockJobManager; + + @Spy private final Context mContext = ApplicationProvider.getApplicationContext(); + + private ImsEntitlementReceiver mReceiver; + private boolean mIsBootUp; + + @Before + public void setUp() throws Exception { + mReceiver = new ImsEntitlementReceiver() { + @Override + protected Dependencies createDependency(Context context, int subId) { + Dependencies dependencies = new Dependencies(); + dependencies.userManager = mMockUserManager; + dependencies.telephonyUtils = mMockTelephonyUtils; + dependencies.jobManager = mMockJobManager; + return dependencies; + } + + @Override + protected boolean isBootUp(Context context, int slotId) { + return mIsBootUp; + } + }; + mIsBootUp = false; + + new EntitlementConfiguration(mContext, LAST_SUB_ID).update(RAW_XML); + new EntitlementConfiguration(mContext, SUB_ID).reset(); + + when(mMockUserManager.isSystemUser()).thenReturn(true); + + setLastSubId(LAST_SUB_ID, 0); + setupCarrierConfig(); + useDirectExecutor(); + } + + @Test + public void onReceive_simChanged_dataReset() { + mReceiver.onReceive(mContext, getCarrierConfigChangedIntent(SUB_ID, /* slotId= */ 0)); + + assertThat( + new EntitlementConfiguration(mContext, LAST_SUB_ID).getVoWifiStatus()).isEqualTo(2); + verify(mMockJobManager, times(1)).queryEntitlementStatusOnceNetworkReady(); + } + + @Test + public void onReceive_theSameSim_dataNotReset() { + mReceiver.onReceive( + mContext, getCarrierConfigChangedIntent(LAST_SUB_ID, /* slotId= */ 0)); + + assertThat( + new EntitlementConfiguration(mContext, LAST_SUB_ID).getVoWifiStatus()).isEqualTo(1); + verify(mMockJobManager, never()).queryEntitlementStatusOnceNetworkReady(); + } + + @Test + public void onReceive_differentSlot_dataNotReset() { + setLastSubId(LAST_SUB_ID, 1); + + mReceiver.onReceive( + mContext, getCarrierConfigChangedIntent(LAST_SUB_ID, /* slotId= */ 1)); + + assertThat( + new EntitlementConfiguration(mContext, LAST_SUB_ID).getVoWifiStatus()).isEqualTo(1); + verify(mMockJobManager, never()).queryEntitlementStatusOnceNetworkReady(); + } + + @Test + public void onReceive_simChangedAndDifferentSlotId_dataReset() { + setLastSubId(LAST_SUB_ID, 1); + + mReceiver.onReceive(mContext, getCarrierConfigChangedIntent(SUB_ID, /* slotId= */ 1)); + + assertThat( + new EntitlementConfiguration(mContext, LAST_SUB_ID).getVoWifiStatus()).isEqualTo(2); + verify(mMockJobManager).queryEntitlementStatusOnceNetworkReady(); + } + + @Test + public void onReceive_isSystemUser_jobScheduled() { + when(mMockUserManager.isSystemUser()).thenReturn(true); + + mReceiver.onReceive( + mContext, getCarrierConfigChangedIntent(SUB_ID, /* slotId= */ 0)); + + verify(mMockJobManager).queryEntitlementStatusOnceNetworkReady(); + } + + @Test + public void onReceive_notSystemUser_noJobScheduled() { + when(mMockUserManager.isSystemUser()).thenReturn(false); + + mReceiver.onReceive( + mContext, getCarrierConfigChangedIntent(SUB_ID, /* slotId= */ 0)); + + verify(mMockJobManager, never()).queryEntitlementStatusOnceNetworkReady(); + } + + @Test + public void onReceive_deviceBootUp_jobScheduled() { + new EntitlementConfiguration(mContext, LAST_SUB_ID).update(RAW_XML_VERSION_0_VALIDITY_0); + mIsBootUp = true; + + mReceiver.onReceive(mContext, getCarrierConfigChangedIntent(LAST_SUB_ID, /* slotId= */ 0)); + + verify(mMockJobManager).queryEntitlementStatusOnceNetworkReady(); + } + + @Test + public void onReceive_bootCompleteInvalidVers_noJobScheduled() { + new EntitlementConfiguration(mContext, LAST_SUB_ID).update(RAW_XML_INVALID_VERS); + mIsBootUp = true; + + mReceiver.onReceive(mContext, getCarrierConfigChangedIntent(LAST_SUB_ID, /* slotId= */ 0)); + + verify(mMockJobManager, never()).queryEntitlementStatusOnceNetworkReady(); + } + + private Intent getCarrierConfigChangedIntent(int subId, int slotId) { + Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); + intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId); + intent.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, slotId); + return intent; + } + + private void setupCarrierConfig() { + PersistableBundle carrierConfig = new PersistableBundle(); + carrierConfig.putBoolean( + CarrierConfigManager.ImsServiceEntitlement.KEY_IMS_PROVISIONING_BOOL, true); + when(mContext.getSystemService(CarrierConfigManager.class)) + .thenReturn(mCarrierConfigManager); + when(mCarrierConfigManager.getConfigForSubId(SUB_ID)).thenReturn(carrierConfig); + when(mCarrierConfigManager.getConfigForSubId(LAST_SUB_ID)).thenReturn(carrierConfig); + } + + private void setLastSubId(int subId, int slotId) { + SharedPreferences preferences = + mContext.getSharedPreferences("PREFERENCE_ACTIVATION_INFO", Context.MODE_PRIVATE); + preferences.edit().putInt("last_sub_id_" + slotId, subId).apply(); + } + + private void useDirectExecutor() throws Exception { + Field field = Executors.class.getDeclaredField("sUseDirectExecutorForTest"); + field.setAccessible(true); + field.set(null, true); + } +} diff --git a/tests/unittests/src/com/android/imsserviceentitlement/WfcActivationControllerTest.java b/tests/unittests/src/com/android/imsserviceentitlement/WfcActivationControllerTest.java index bd78b14..72b9341 100644 --- a/tests/unittests/src/com/android/imsserviceentitlement/WfcActivationControllerTest.java +++ b/tests/unittests/src/com/android/imsserviceentitlement/WfcActivationControllerTest.java @@ -39,6 +39,7 @@ import com.android.imsserviceentitlement.ts43.Ts43VowifiStatus.AddrStatus; import com.android.imsserviceentitlement.ts43.Ts43VowifiStatus.EntitlementStatus; import com.android.imsserviceentitlement.ts43.Ts43VowifiStatus.ProvStatus; import com.android.imsserviceentitlement.ts43.Ts43VowifiStatus.TcStatus; +import com.android.imsserviceentitlement.utils.Executors; import org.junit.Before; import org.junit.Rule; @@ -79,7 +80,7 @@ public class WfcActivationControllerTest { when(mTelephonyManager.createForSubscriptionId(SUB_ID)).thenReturn(mTelephonyManager); setNetworkConnected(true); - Field field = EntitlementUtils.class.getDeclaredField("sUseDirectExecutorForTest"); + Field field = Executors.class.getDeclaredField("sUseDirectExecutorForTest"); field.setAccessible(true); field.set(null, true); } |