1 /* 2 * Copyright (C) 2021 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.imsserviceentitlement; 18 19 import static android.telephony.TelephonyManager.SIM_STATE_LOADED; 20 21 import static com.android.imsserviceentitlement.entitlement.EntitlementConfiguration.ClientBehavior.NEEDS_TO_RESET; 22 import static com.android.imsserviceentitlement.entitlement.EntitlementConfiguration.ClientBehavior.VALID_DURING_VALIDITY; 23 import static com.android.imsserviceentitlement.entitlement.EntitlementConfiguration.ClientBehavior.VALID_WITHOUT_DURATION; 24 import static com.android.imsserviceentitlement.utils.Executors.getAsyncExecutor; 25 26 import android.content.BroadcastReceiver; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.content.SharedPreferences; 30 import android.os.UserManager; 31 import android.provider.Settings; 32 import android.telephony.CarrierConfigManager; 33 import android.telephony.SubscriptionManager; 34 import android.util.Log; 35 36 import androidx.annotation.VisibleForTesting; 37 import androidx.annotation.WorkerThread; 38 39 import com.android.imsserviceentitlement.entitlement.EntitlementConfiguration; 40 import com.android.imsserviceentitlement.entitlement.EntitlementConfiguration.ClientBehavior; 41 import com.android.imsserviceentitlement.job.JobManager; 42 import com.android.imsserviceentitlement.utils.TelephonyUtils; 43 44 /** Watches events and manages service entitlement polling. */ 45 public class ImsEntitlementReceiver extends BroadcastReceiver { 46 private static final String TAG = "IMSSE-ImsEntitlementReceiver"; 47 48 /** 49 * Shared preference name for activation information, the key used in this file should append 50 * slot id if the value depended on carrier. 51 */ 52 private static final String PREFERENCE_ACTIVATION_INFO = "PREFERENCE_ACTIVATION_INFO"; 53 /** 54 * Shared preference key for last known subscription id of a SIM slot; default value {@link 55 * SubscriptionManager#INVALID_SUBSCRIPTION_ID}. 56 */ 57 private static final String KEY_LAST_SUB_ID = "last_sub_id_"; 58 /** Shared preference key for last boot count. */ 59 private static final String KEY_LAST_BOOT_COUNT = "last_boot_count_"; 60 61 @Override onReceive(Context context, Intent intent)62 public void onReceive(Context context, Intent intent) { 63 int currentSubId = 64 intent.getIntExtra( 65 SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, 66 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 67 int slotId = 68 intent.getIntExtra( 69 SubscriptionManager.EXTRA_SLOT_INDEX, 70 SubscriptionManager.INVALID_SIM_SLOT_INDEX); 71 Dependencies dependencies = createDependency(context, currentSubId); 72 if (!dependencies.userManager.isSystemUser() 73 || !SubscriptionManager.isValidSubscriptionId(currentSubId) 74 || dependencies.telephonyUtils.getSimApplicationState() != SIM_STATE_LOADED 75 || !TelephonyUtils.isImsProvisioningRequired(context, currentSubId)) { 76 return; 77 } 78 79 String action = intent.getAction(); 80 if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action)) { 81 final PendingResult result = goAsync(); 82 getAsyncExecutor().execute( 83 () -> handleCarrierConfigChanged( 84 context, currentSubId, slotId, dependencies.jobManager, result)); 85 } 86 } 87 88 /** 89 * Handles the event of SIM change and device boot up while receiving {@link 90 * CarrierConfigManager#ACTION_CARRIER_CONFIG_CHANGED}. 91 */ 92 @WorkerThread handleCarrierConfigChanged( Context context, int currentSubId, int slotId, JobManager jobManager, PendingResult result)93 private void handleCarrierConfigChanged( 94 Context context, int currentSubId, int slotId, JobManager jobManager, 95 PendingResult result) { 96 boolean shouldQuery = false; 97 98 // Handle device boot up. 99 if (isBootUp(context, slotId)) { 100 ClientBehavior clientBehavior = 101 new EntitlementConfiguration(context, currentSubId).entitlementValidation(); 102 Log.d(TAG, "Device boot up, clientBehavior=" + clientBehavior); 103 if (clientBehavior == VALID_DURING_VALIDITY 104 || clientBehavior == VALID_WITHOUT_DURATION 105 || clientBehavior == NEEDS_TO_RESET) { 106 shouldQuery = true; 107 } 108 } 109 110 // Handle SIM changed. 111 int lastSubId = getAndSetSubId(context, currentSubId, slotId); 112 if (currentSubId != lastSubId) { 113 Log.d(TAG, 114 "SubId for slot " + slotId + " changed: " + lastSubId + " -> " + currentSubId); 115 if (SubscriptionManager.isValidSubscriptionId(lastSubId)) { 116 new EntitlementConfiguration(context, lastSubId).reset(); 117 } 118 shouldQuery = true; 119 } 120 121 if (shouldQuery) { 122 jobManager.queryEntitlementStatusOnceNetworkReady(); 123 } 124 125 if (result != null) { 126 result.finish(); 127 } 128 } 129 130 /** 131 * Returns {@code true} if current boot count greater than previous one. Saves the latest boot 132 * count into shared preference. 133 */ 134 @VisibleForTesting isBootUp(Context context, int slotId)135 boolean isBootUp(Context context, int slotId) { 136 SharedPreferences preferences = 137 context.getSharedPreferences(PREFERENCE_ACTIVATION_INFO, Context.MODE_PRIVATE); 138 int lastBootCount = preferences.getInt(KEY_LAST_BOOT_COUNT + slotId, 0); 139 int currentBootCount = 140 Settings.Global.getInt( 141 context.getContentResolver(), Settings.Global.BOOT_COUNT, /* def= */ -1); 142 preferences.edit().putInt(KEY_LAST_BOOT_COUNT + slotId, currentBootCount).apply(); 143 144 return currentBootCount != lastBootCount; 145 } 146 getAndSetSubId(Context context, int currentSubId, int slotId)147 private int getAndSetSubId(Context context, int currentSubId, int slotId) { 148 SharedPreferences preferences = 149 context.getSharedPreferences(PREFERENCE_ACTIVATION_INFO, Context.MODE_PRIVATE); 150 int lastSubId = preferences.getInt( 151 KEY_LAST_SUB_ID + slotId, SubscriptionManager.INVALID_SUBSCRIPTION_ID); 152 preferences.edit().putInt(KEY_LAST_SUB_ID + slotId, currentSubId).apply(); 153 return lastSubId; 154 } 155 156 /** Returns initialized dependencies */ 157 @VisibleForTesting createDependency(Context context, int subId)158 Dependencies createDependency(Context context, int subId) { 159 // Wrap return value 160 Dependencies ret = new Dependencies(); 161 ret.telephonyUtils = new TelephonyUtils(context, subId); 162 ret.userManager = context.getSystemService(UserManager.class); 163 ret.jobManager = 164 JobManager.getInstance(context, ImsEntitlementPollingService.COMPONENT_NAME, subId); 165 return ret; 166 } 167 168 /** A collection of dependency objects */ 169 protected static class Dependencies { 170 public TelephonyUtils telephonyUtils; 171 public UserManager userManager; 172 public JobManager jobManager; 173 } 174 } 175