• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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