1 /* 2 * Copyright (C) 2024 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 package com.android.nfc.cardemulation; 17 18 import android.content.Context; 19 import android.content.SharedPreferences; 20 import android.content.pm.PackageManager; 21 import android.telephony.SubscriptionInfo; 22 import android.util.Log; 23 24 import com.android.nfc.R; 25 import com.android.nfc.cardemulation.util.TelephonyUtils; 26 27 import java.util.List; 28 import java.util.stream.Collectors; 29 30 public class PreferredSubscriptionService implements TelephonyUtils.Callback { 31 static final String TAG = "PreferredSubscriptionService"; 32 static final String PREF_SUBSCRIPTION = "SubscriptionPref"; 33 static final String PREF_PREFERRED_SUB_ID = "pref_sub_id"; 34 private SharedPreferences mSubscriptionPrefs = null; 35 36 Context mContext; 37 Callback mCallback; 38 39 int mDefaultSubscriptionId = TelephonyUtils.SUBSCRIPTION_ID_UNKNOWN; 40 boolean mIsEuiccCapable; 41 boolean mIsUiccCapable; 42 TelephonyUtils mTelephonyUtils; 43 int mActiveSubscriptoinState = TelephonyUtils.SUBSCRIPTION_STATE_UNKNOWN; 44 List<SubscriptionInfo> mActiveSubscriptions = null; 45 46 public interface Callback { onPreferredSubscriptionChanged(int subscriptionId, boolean isActive)47 void onPreferredSubscriptionChanged(int subscriptionId, boolean isActive); 48 } 49 PreferredSubscriptionService(Context context, Callback callback)50 public PreferredSubscriptionService(Context context, Callback callback) { 51 mContext = context; 52 mCallback = callback; 53 54 mIsUiccCapable = context.getPackageManager().hasSystemFeature( 55 PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC); 56 mIsEuiccCapable = mContext.getResources().getBoolean(R.bool.enable_euicc_support); 57 58 mTelephonyUtils = TelephonyUtils.getInstance(context); 59 mSubscriptionPrefs = mContext.getSharedPreferences( 60 PREF_SUBSCRIPTION, Context.MODE_PRIVATE); 61 62 // Initialize default subscription to UICC if there is no preference 63 if (mIsUiccCapable || mIsEuiccCapable) { 64 mDefaultSubscriptionId = getPreferredSubscriptionId(); 65 if (mDefaultSubscriptionId == TelephonyUtils.SUBSCRIPTION_ID_UNKNOWN) { 66 Log.d(TAG, "PreferredSubscriptionService: Set preferred subscription " 67 + "to UICC, only update"); 68 setPreferredSubscriptionId(TelephonyUtils.SUBSCRIPTION_ID_UICC, false); 69 } 70 } 71 } 72 initialize()73 public void initialize() { 74 if (mIsUiccCapable || mIsEuiccCapable) { 75 onDefaultSubscriptionChanged(); 76 mTelephonyUtils.registerSubscriptionChangedCallback(this); 77 } 78 } 79 getPreferredSubscriptionId()80 public int getPreferredSubscriptionId() { 81 Log.d(TAG, "getPreferredSubscriptionId: " + mDefaultSubscriptionId); 82 return mSubscriptionPrefs.getInt( 83 PREF_PREFERRED_SUB_ID, TelephonyUtils.SUBSCRIPTION_ID_UNKNOWN); 84 } 85 setPreferredSubscriptionId(int subscriptionId, boolean force)86 public void setPreferredSubscriptionId(int subscriptionId, boolean force) { 87 Log.d(TAG, "setPreferredSubscriptionId: " + subscriptionId); 88 if (mDefaultSubscriptionId != subscriptionId) { 89 mDefaultSubscriptionId = subscriptionId; 90 mSubscriptionPrefs.edit().putInt(PREF_PREFERRED_SUB_ID, subscriptionId).commit(); 91 if (force) { 92 onDefaultSubscriptionChanged(); 93 } 94 } 95 } 96 onDefaultSubscriptionChanged()97 public void onDefaultSubscriptionChanged() { 98 Log.d(TAG, "onDefaultSubscriptionChanged: "); 99 boolean isSubscriptionActivated = isSubscriptionActivated(mDefaultSubscriptionId); 100 mActiveSubscriptoinState = isSubscriptionActivated ? 101 TelephonyUtils.SUBSCRIPTION_STATE_ACTIVATE 102 : TelephonyUtils.SUBSCRIPTION_STATE_INACTIVATE; 103 mCallback.onPreferredSubscriptionChanged(mDefaultSubscriptionId, isSubscriptionActivated); 104 } 105 106 @Override onActiveSubscriptionsUpdated(List<SubscriptionInfo> activeSubscriptionList)107 public void onActiveSubscriptionsUpdated(List<SubscriptionInfo> activeSubscriptionList) { 108 boolean isActivationStateChanged = checkSubscriptionStateChanged(activeSubscriptionList); 109 if (isActivationStateChanged) { 110 mCallback.onPreferredSubscriptionChanged(mDefaultSubscriptionId, 111 mActiveSubscriptoinState == TelephonyUtils.SUBSCRIPTION_STATE_ACTIVATE); 112 } else { 113 Log.i(TAG, "onActiveSubscriptionsUpdated: Active Subscription is not changed"); 114 } 115 } 116 isSubscriptionActivated(int subscriptionId)117 private boolean isSubscriptionActivated(int subscriptionId) { 118 if (mActiveSubscriptions == null) { 119 Log.d(TAG, "isSubscriptionActivated: get active subscriptions is " 120 + "list because it's null"); 121 mActiveSubscriptions = mTelephonyUtils.getActiveSubscriptions().stream().filter( 122 TelephonyUtils.SUBSCRIPTION_ACTIVE_CONDITION_FOR_UICC.or( 123 TelephonyUtils.SUBSCRIPTION_ACTIVE_CONDITION_FOR_EUICC)) 124 .collect(Collectors.toList()); 125 } 126 boolean isEuiccSubscription = mTelephonyUtils.isEuiccSubscription(subscriptionId); 127 return mActiveSubscriptions.stream().anyMatch(subscriptionInfo -> 128 subscriptionInfo.isEmbedded() == isEuiccSubscription); 129 } 130 checkSubscriptionStateChanged(List<SubscriptionInfo> activeSubscriptionList)131 private boolean checkSubscriptionStateChanged(List<SubscriptionInfo> activeSubscriptionList) { 132 // filtered subscriptions 133 mActiveSubscriptions = activeSubscriptionList.stream().filter( 134 TelephonyUtils.SUBSCRIPTION_ACTIVE_CONDITION_FOR_UICC.or( 135 TelephonyUtils.SUBSCRIPTION_ACTIVE_CONDITION_FOR_EUICC)) 136 .collect(Collectors.toList()); 137 int previousActiveSubscriptionState = mActiveSubscriptoinState; 138 int currentActiveSubscriptionState = isSubscriptionActivated(mDefaultSubscriptionId) ? 139 TelephonyUtils.SUBSCRIPTION_STATE_ACTIVATE : 140 TelephonyUtils.SUBSCRIPTION_STATE_INACTIVATE; 141 if (previousActiveSubscriptionState != currentActiveSubscriptionState) { 142 Log.d(TAG, "checkSubscriptionStateChanged: state changed: " 143 + previousActiveSubscriptionState + " to " + currentActiveSubscriptionState); 144 mActiveSubscriptoinState = currentActiveSubscriptionState; 145 return true; 146 } 147 return false; 148 } 149 } 150