1 /* 2 * Copyright (C) 2013 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.sysprop.NfcProperties; 22 import android.text.TextUtils; 23 import android.util.Log; 24 25 import androidx.annotation.VisibleForTesting; 26 27 import com.android.nfc.DeviceConfigFacade; 28 import com.android.nfc.NfcService; 29 import com.android.nfc.cardemulation.util.TelephonyUtils; 30 import com.android.nfc.dhimpl.NativeNfcManager; 31 32 import java.util.Arrays; 33 import java.util.HashMap; 34 import java.util.Map; 35 import java.util.Optional; 36 37 public class RoutingOptionManager { 38 static final String TAG = "RoutingOptionManager"; 39 static final boolean DBG = NfcProperties.debug_enabled().orElse(true); 40 41 static final int ROUTE_UNKNOWN = -1; 42 static final int ROUTE_DEFAULT = -2; 43 44 public static final String DEVICE_HOST = "DH"; 45 public static final String SE_NDEF_NFCEE = "NDEF-NFCEE"; 46 public static final String SE_PREFIX_SIM = "SIM"; 47 public static final String SE_PREFIX_ESE = "eSE"; 48 49 public static final String PREF_ROUTING_OPTIONS = "RoutingOptionPrefs"; 50 public static final String KEY_DEFAULT_ROUTE = "default_route"; 51 public static final String KEY_DEFAULT_ISO_DEP_ROUTE = "default_iso_dep_route"; 52 public static final String KEY_DEFAULT_OFFHOST_ROUTE = "default_offhost_route"; 53 public static final String KEY_DEFAULT_SC_ROUTE = "default_sc_route"; 54 public static final String KEY_AUTO_CHANGE_CAPABLE = "allow_auto_routing_changed"; 55 Context mContext; 56 private SharedPreferences mPrefs; 57 58 int mDefaultRoute; 59 int mDefaultIsoDepRoute; 60 int mDefaultOffHostRoute; 61 int mDefaultFelicaRoute; 62 int mDefaultScRoute; 63 int mNdefNfceeRoute; 64 final byte[] mOffHostRouteUicc; 65 final byte[] mOffHostRouteEse; 66 final int mAidMatchingSupport; 67 68 int mOverrideDefaultRoute = ROUTE_UNKNOWN; 69 int mOverrideDefaultIsoDepRoute = ROUTE_UNKNOWN; 70 int mOverrideDefaultOffHostRoute = ROUTE_UNKNOWN; 71 int mOverrideDefaultFelicaRoute = ROUTE_UNKNOWN; 72 int mOverrideDefaultScRoute = ROUTE_UNKNOWN; 73 74 boolean mIsRoutingTableOverrided = false; 75 76 boolean mIsAutoChangeCapable = true; 77 boolean mIsUiccCapable = false; 78 boolean mIsEseCapable = false; 79 80 // Look up table for secure element name to route id 81 HashMap<String, Integer> mRouteForSecureElement = new HashMap<>(); 82 83 // Look up table for route id to secure element name 84 HashMap<Integer, String> mSecureElementForRoute = new HashMap<>(); 85 86 // Helper class for SIM routing values 87 static class SimSettings { 88 int type; 89 int index; 90 // Up to two eUICCs can be supported depending on whether MEP is supported or not. 91 int[] eUiccIndexs = new int[2]; SimSettings(int numberOfUicc, int startIndexOfEuicc)92 SimSettings(int numberOfUicc, int startIndexOfEuicc) { 93 type = TelephonyUtils.SIM_TYPE_UNKNOWN; 94 index = 0; 95 if (numberOfUicc != 0 && numberOfUicc > startIndexOfEuicc) { 96 eUiccIndexs[0] = startIndexOfEuicc; 97 eUiccIndexs[1] = (numberOfUicc > startIndexOfEuicc + 1) ? 98 startIndexOfEuicc + 1 : startIndexOfEuicc; 99 } 100 } getName()101 String getName() { 102 return SE_PREFIX_SIM + (index + 1); 103 } setType(int type)104 void setType(int type) { 105 this.type = type; 106 setIndex(); 107 } setIndex()108 private void setIndex() { 109 switch (type) { 110 case TelephonyUtils.SIM_TYPE_UICC: 111 index = 0; 112 break; 113 case TelephonyUtils.SIM_TYPE_EUICC_1: 114 index = eUiccIndexs[0]; 115 break; 116 case TelephonyUtils.SIM_TYPE_EUICC_2: 117 index = eUiccIndexs[1]; 118 break; 119 default: 120 break; 121 } 122 } 123 } 124 125 SimSettings mPreferredSimSettings; 126 127 int mMepMode; 128 @VisibleForTesting doGetDefaultRouteDestination()129 native int doGetDefaultRouteDestination(); 130 @VisibleForTesting doGetDefaultIsoDepRouteDestination()131 native int doGetDefaultIsoDepRouteDestination(); 132 @VisibleForTesting doGetDefaultOffHostRouteDestination()133 native int doGetDefaultOffHostRouteDestination(); 134 @VisibleForTesting doGetDefaultFelicaRouteDestination()135 native int doGetDefaultFelicaRouteDestination(); 136 @VisibleForTesting doGetDefaultScRouteDestination()137 native int doGetDefaultScRouteDestination(); 138 @VisibleForTesting doGetOffHostUiccDestination()139 native byte[] doGetOffHostUiccDestination(); 140 @VisibleForTesting doGetOffHostEseDestination()141 native byte[] doGetOffHostEseDestination(); 142 @VisibleForTesting doGetAidMatchingMode()143 native int doGetAidMatchingMode(); doGetEuiccMepMode()144 native int doGetEuiccMepMode(); 145 146 private static RoutingOptionManager sInstance; 147 getInstance()148 public static RoutingOptionManager getInstance() { 149 if (sInstance == null) { 150 sInstance = new RoutingOptionManager(); 151 } 152 return sInstance; 153 } 154 155 @VisibleForTesting RoutingOptionManager()156 RoutingOptionManager() { 157 mDefaultRoute = doGetDefaultRouteDestination(); 158 if (DBG) Log.d(TAG, "mDefaultRoute=0x" + Integer.toHexString(mDefaultRoute)); 159 mDefaultIsoDepRoute = doGetDefaultIsoDepRouteDestination(); 160 if (DBG) Log.d(TAG, "mDefaultIsoDepRoute=0x" + Integer.toHexString(mDefaultIsoDepRoute)); 161 mDefaultOffHostRoute = doGetDefaultOffHostRouteDestination(); 162 if (DBG) Log.d(TAG, "mDefaultOffHostRoute=0x" + Integer.toHexString(mDefaultOffHostRoute)); 163 mDefaultFelicaRoute = doGetDefaultFelicaRouteDestination(); 164 if (DBG) Log.d(TAG, "mDefaultFelicaRoute=0x" + Integer.toHexString(mDefaultFelicaRoute)); 165 mDefaultScRoute = doGetDefaultScRouteDestination(); 166 if (DBG) Log.d(TAG, "mDefaultScRoute=0x" + Integer.toHexString(mDefaultScRoute)); 167 mOffHostRouteUicc = doGetOffHostUiccDestination(); 168 if (DBG) Log.d(TAG, "mOffHostRouteUicc=" + Arrays.toString(mOffHostRouteUicc)); 169 mOffHostRouteEse = doGetOffHostEseDestination(); 170 if (DBG) Log.d(TAG, "mOffHostRouteEse=" + Arrays.toString(mOffHostRouteEse)); 171 mAidMatchingSupport = doGetAidMatchingMode(); 172 if (DBG) Log.d(TAG, "mAidMatchingSupport=0x" + Integer.toHexString(mAidMatchingSupport)); 173 mNdefNfceeRoute = NativeNfcManager.getInstance().getNdefNfceeRouteId(); 174 if (DBG) Log.d(TAG, "mNdefNfceeRoute=0x" + Integer.toHexString(mNdefNfceeRoute)); 175 176 mPreferredSimSettings = new SimSettings((mOffHostRouteUicc != null) ? 177 mOffHostRouteUicc.length : 0, 1); 178 mMepMode = doGetEuiccMepMode(); 179 createLookUpTable(); 180 } 181 overwriteRoutingTable()182 public void overwriteRoutingTable() { 183 Log.d(TAG, "overwriteRoutingTable()"); 184 if (mOverrideDefaultRoute != ROUTE_UNKNOWN) { 185 if (mOverrideDefaultRoute == ROUTE_DEFAULT) { 186 Log.i(TAG, "overwriteRoutingTable: overwrite mDefaultRoute with " 187 + "default config value"); 188 mDefaultRoute = doGetDefaultRouteDestination(); 189 } else { 190 Log.d(TAG, "overwriteRoutingTable: mDefaultRoute : " 191 + Integer.toHexString(mOverrideDefaultRoute)); 192 mDefaultRoute = mOverrideDefaultRoute; 193 } 194 writeRoutingOption(KEY_DEFAULT_ROUTE, getSecureElementForRoute(mDefaultRoute)); 195 } 196 197 if (mOverrideDefaultIsoDepRoute != ROUTE_UNKNOWN) { 198 if (mOverrideDefaultIsoDepRoute == ROUTE_DEFAULT) { 199 Log.i(TAG, "overwriteRoutingTable: overwrite mDefaultIsoDepRoute " 200 + "with default config value"); 201 mDefaultIsoDepRoute = doGetDefaultIsoDepRouteDestination(); 202 } else { 203 Log.d(TAG, "overwriteRoutingTable: mDefaultIsoDepRoute : " 204 + Integer.toHexString(mOverrideDefaultIsoDepRoute)); 205 mDefaultIsoDepRoute = mOverrideDefaultIsoDepRoute; 206 } 207 writeRoutingOption( 208 KEY_DEFAULT_ISO_DEP_ROUTE, getSecureElementForRoute(mDefaultIsoDepRoute)); 209 } 210 211 if (mOverrideDefaultOffHostRoute != ROUTE_UNKNOWN) { 212 if (mOverrideDefaultOffHostRoute == ROUTE_DEFAULT) { 213 Log.i(TAG, "overwriteRoutingTable: overwrite mDefaultOffHostRoute with " 214 + "default config value"); 215 mDefaultOffHostRoute = doGetDefaultOffHostRouteDestination(); 216 } else { 217 Log.d(TAG, "overwriteRoutingTable: mDefaultOffHostRoute : " 218 + Integer.toHexString(mOverrideDefaultOffHostRoute)); 219 mDefaultOffHostRoute = mOverrideDefaultOffHostRoute; 220 } 221 writeRoutingOption( 222 KEY_DEFAULT_OFFHOST_ROUTE, getSecureElementForRoute(mDefaultOffHostRoute)); 223 } 224 225 if (mOverrideDefaultScRoute != ROUTE_UNKNOWN) { 226 if (mOverrideDefaultScRoute == ROUTE_DEFAULT) { 227 Log.i(TAG, "overwriteRoutingTable: mDefaultScRoute with default config value"); 228 mDefaultScRoute = doGetDefaultScRouteDestination(); 229 } else { 230 Log.d(TAG, "overwriteRoutingTable: mDefaultScRoute : " 231 + Integer.toHexString(mOverrideDefaultScRoute)); 232 mDefaultScRoute = mOverrideDefaultScRoute; 233 } 234 writeRoutingOption( 235 KEY_DEFAULT_SC_ROUTE, getSecureElementForRoute(mDefaultScRoute)); 236 } 237 238 mOverrideDefaultRoute = mOverrideDefaultIsoDepRoute = mOverrideDefaultOffHostRoute = 239 mOverrideDefaultScRoute = mOverrideDefaultFelicaRoute = ROUTE_UNKNOWN; 240 } 241 overrideDefaultRoute(int defaultRoute)242 public void overrideDefaultRoute(int defaultRoute) { 243 mOverrideDefaultRoute = defaultRoute; 244 } 245 overrideDefaultIsoDepRoute(int isoDepRoute)246 public void overrideDefaultIsoDepRoute(int isoDepRoute) { 247 mOverrideDefaultIsoDepRoute = isoDepRoute; 248 NfcService.getInstance().setIsoDepProtocolRoute(isoDepRoute); 249 } 250 overrideDefaultOffHostRoute(int offHostRoute)251 public void overrideDefaultOffHostRoute(int offHostRoute) { 252 mOverrideDefaultOffHostRoute = offHostRoute; 253 mOverrideDefaultFelicaRoute = offHostRoute; 254 NfcService.getInstance().setTechnologyABFRoute(offHostRoute, offHostRoute); 255 } 256 overrideDefaultScRoute(int scRoute)257 public void overrideDefaultScRoute(int scRoute) { 258 mOverrideDefaultScRoute = scRoute; 259 NfcService.getInstance().setSystemCodeRoute(scRoute); 260 } 261 recoverOverridedRoutingTable()262 public void recoverOverridedRoutingTable() { 263 NfcService.getInstance().setIsoDepProtocolRoute(mDefaultIsoDepRoute); 264 NfcService.getInstance().setTechnologyABFRoute(mDefaultOffHostRoute, mDefaultFelicaRoute); 265 mOverrideDefaultRoute = mOverrideDefaultIsoDepRoute = mOverrideDefaultOffHostRoute = 266 mOverrideDefaultFelicaRoute = ROUTE_UNKNOWN; 267 } 268 getOverrideDefaultRoute()269 public int getOverrideDefaultRoute() { 270 return mOverrideDefaultRoute; 271 } 272 getDefaultRoute()273 public int getDefaultRoute() { 274 return getAlternativeRouteIfSimIsInvalid(mDefaultRoute); 275 } 276 getOverrideDefaultIsoDepRoute()277 public int getOverrideDefaultIsoDepRoute() { 278 return mOverrideDefaultIsoDepRoute; 279 } 280 getDefaultIsoDepRoute()281 public int getDefaultIsoDepRoute() { 282 return getAlternativeRouteIfSimIsInvalid(mDefaultIsoDepRoute); 283 } 284 getOverrideDefaultOffHostRoute()285 public int getOverrideDefaultOffHostRoute() { 286 return mOverrideDefaultOffHostRoute; 287 } 288 getDefaultOffHostRoute()289 public int getDefaultOffHostRoute() { 290 return getAlternativeRouteIfSimIsInvalid(mDefaultOffHostRoute); 291 } 292 getDefaultFelicaRoute()293 public int getDefaultFelicaRoute() { 294 return mDefaultFelicaRoute; 295 } 296 getOverrideDefaultFelicaRoute()297 public int getOverrideDefaultFelicaRoute() { 298 return mOverrideDefaultFelicaRoute; 299 } 300 getOverrideDefaultScRoute()301 public int getOverrideDefaultScRoute() { 302 return mOverrideDefaultScRoute; 303 } 304 getDefaultScRoute()305 public int getDefaultScRoute() { 306 return mDefaultScRoute; 307 } 308 getOffHostRouteUicc()309 public byte[] getOffHostRouteUicc() { 310 return mOffHostRouteUicc; 311 } 312 getOffHostRouteEse()313 public byte[] getOffHostRouteEse() { 314 return mOffHostRouteEse; 315 } 316 getAidMatchingSupport()317 public int getAidMatchingSupport() { 318 return mAidMatchingSupport; 319 } 320 getMepMode()321 public int getMepMode() { return mMepMode;} 322 isRoutingTableOverrided()323 public boolean isRoutingTableOverrided() { 324 return mOverrideDefaultRoute != ROUTE_UNKNOWN 325 || mOverrideDefaultIsoDepRoute != ROUTE_UNKNOWN 326 || mOverrideDefaultOffHostRoute != ROUTE_UNKNOWN 327 || mOverrideDefaultFelicaRoute != ROUTE_UNKNOWN 328 || mOverrideDefaultScRoute != ROUTE_UNKNOWN; 329 } 330 createLookUpTable()331 private void createLookUpTable() { 332 mRouteForSecureElement.putIfAbsent(DEVICE_HOST, 0); 333 mSecureElementForRoute.put(0, DEVICE_HOST); 334 335 mRouteForSecureElement.putIfAbsent("UNKNOWN", ROUTE_UNKNOWN); 336 mSecureElementForRoute.put(ROUTE_UNKNOWN, "UNKNOWN"); 337 338 mRouteForSecureElement.putIfAbsent("default", ROUTE_DEFAULT); 339 mSecureElementForRoute.put(ROUTE_DEFAULT, "default"); 340 341 mRouteForSecureElement.putIfAbsent(SE_NDEF_NFCEE, mNdefNfceeRoute); 342 mSecureElementForRoute.put(mNdefNfceeRoute, SE_NDEF_NFCEE); 343 344 addOrUpdateTableItems(SE_PREFIX_SIM, mOffHostRouteUicc); 345 addOrUpdateTableItems(SE_PREFIX_ESE, mOffHostRouteEse); 346 } 347 isRoutingTableOverwrittenOrOverlaid( DeviceConfigFacade deviceConfigFacade, SharedPreferences prefs)348 boolean isRoutingTableOverwrittenOrOverlaid( 349 DeviceConfigFacade deviceConfigFacade, SharedPreferences prefs) { 350 return !TextUtils.isEmpty(deviceConfigFacade.getDefaultRoute()) 351 || !TextUtils.isEmpty(deviceConfigFacade.getDefaultIsoDepRoute()) 352 || !TextUtils.isEmpty(deviceConfigFacade.getDefaultOffHostRoute()) 353 || !TextUtils.isEmpty(deviceConfigFacade.getDefaultScRoute()) 354 || !prefs.getAll().isEmpty(); 355 } 356 readRoutingOptionsFromPrefs( Context context, DeviceConfigFacade deviceConfigFacade)357 public void readRoutingOptionsFromPrefs( 358 Context context, DeviceConfigFacade deviceConfigFacade) { 359 Log.d(TAG, "readRoutingOptionsFromPrefs"); 360 if (mPrefs == null) { 361 Log.d(TAG, "readRoutingOptionsFromPrefs: create mPrefs in readRoutingOptions"); 362 mContext = context; 363 mPrefs = context.getSharedPreferences(PREF_ROUTING_OPTIONS, Context.MODE_PRIVATE); 364 mIsUiccCapable = context.getPackageManager().hasSystemFeature( 365 PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC); 366 mIsEseCapable = context.getPackageManager().hasSystemFeature( 367 PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE); 368 } 369 370 // If the OEM does not set default routes in the overlay and if no app has overwritten 371 // the routing table using `overwriteRoutingTable`, skip this preference reading. 372 if (!isRoutingTableOverwrittenOrOverlaid(deviceConfigFacade, mPrefs)) { 373 Log.d(TAG, "readRoutingOptionsFromPrefs: Routing table not overwritten or overlaid"); 374 return; 375 } 376 377 // read default route 378 if (!mPrefs.contains(KEY_DEFAULT_ROUTE)) { 379 writeRoutingOption(KEY_DEFAULT_ROUTE, deviceConfigFacade.getDefaultRoute()); 380 } 381 mDefaultRoute = getRouteForSecureElement(mPrefs.getString(KEY_DEFAULT_ROUTE, null)); 382 383 // read default iso dep route 384 if (!mPrefs.contains(KEY_DEFAULT_ISO_DEP_ROUTE)) { 385 writeRoutingOption( 386 KEY_DEFAULT_ISO_DEP_ROUTE, deviceConfigFacade.getDefaultIsoDepRoute()); 387 } 388 mDefaultIsoDepRoute = 389 getRouteForSecureElement(mPrefs.getString(KEY_DEFAULT_ISO_DEP_ROUTE, null)); 390 391 // read default offhost route 392 if (!mPrefs.contains(KEY_DEFAULT_OFFHOST_ROUTE)) { 393 writeRoutingOption( 394 KEY_DEFAULT_OFFHOST_ROUTE, deviceConfigFacade.getDefaultOffHostRoute()); 395 } 396 mDefaultOffHostRoute = 397 getRouteForSecureElement(mPrefs.getString(KEY_DEFAULT_OFFHOST_ROUTE, null)); 398 399 // read default system code route 400 if (!mPrefs.contains(KEY_DEFAULT_SC_ROUTE)) { 401 writeRoutingOption( 402 KEY_DEFAULT_SC_ROUTE, deviceConfigFacade.getDefaultScRoute()); 403 } 404 mDefaultScRoute = 405 getRouteForSecureElement(mPrefs.getString(KEY_DEFAULT_SC_ROUTE, null)); 406 407 // read auto change capable 408 if (!mPrefs.contains(KEY_AUTO_CHANGE_CAPABLE)) { 409 writeRoutingOption(KEY_AUTO_CHANGE_CAPABLE, true); 410 } 411 mIsAutoChangeCapable = mPrefs.getBoolean(KEY_AUTO_CHANGE_CAPABLE, true); 412 Log.d(TAG, "readRoutingOptionsFromPrefs: ReadOptions - " + toString()); 413 } 414 setAutoChangeStatus(boolean status)415 public void setAutoChangeStatus(boolean status) { 416 mIsAutoChangeCapable = status; 417 } 418 isAutoChangeEnabled()419 public boolean isAutoChangeEnabled() { 420 return mIsAutoChangeCapable; 421 } 422 writeRoutingOption(String key, String name)423 private void writeRoutingOption(String key, String name) { 424 mPrefs.edit().putString(key, name).apply(); 425 } 426 writeRoutingOption(String key, boolean value)427 private void writeRoutingOption(String key, boolean value) { 428 mPrefs.edit().putBoolean(key, value).apply(); 429 } 430 getRouteForSecureElement(String se)431 public int getRouteForSecureElement(String se) { 432 return Optional.ofNullable(mRouteForSecureElement.get(renameSecureElementIfSimType(se))) 433 .orElseGet(() -> 0x00); 434 } 435 getSecureElementForRoute(int route)436 public String getSecureElementForRoute(int route) { 437 return Optional.ofNullable(mSecureElementForRoute.get(route)).orElseGet(()->DEVICE_HOST); 438 } 439 440 // Implement eSIM support onPreferredSimChanged(int simType)441 public void onPreferredSimChanged(int simType) { 442 mPreferredSimSettings.setType(simType); 443 } getPreferredSim()444 public String getPreferredSim() { 445 return mPreferredSimSettings.getName(); 446 } renameSecureElementIfSimType(String seName)447 public String renameSecureElementIfSimType(String seName) { 448 return seName.startsWith(SE_PREFIX_SIM) ? mPreferredSimSettings.getName() : seName; 449 } getAlternativeRouteIfSimIsInvalid(int route)450 private int getAlternativeRouteIfSimIsInvalid(int route) { 451 // TODO - Implement 452 if (getSecureElementForRoute(route).startsWith(SE_PREFIX_SIM)) { 453 if (mPreferredSimSettings.type == TelephonyUtils.SIM_TYPE_UNKNOWN) { 454 Log.e(TAG, "getAlternativeRouteIfSimIsInvalid: sim is invalid"); 455 return getRouteForSecureElement(mIsEseCapable ? (SE_PREFIX_ESE + 1) : DEVICE_HOST); 456 } 457 } 458 return route; 459 } 460 // Implement eSIM support addOrUpdateTableItems(String prefix, byte[] routes)461 private void addOrUpdateTableItems(String prefix, byte[] routes) { 462 if (routes != null && routes.length != 0) { 463 for (int index = 1; index <= routes.length; index++) { 464 int route = routes[index - 1] & 0xFF; 465 String name = prefix + index; 466 mRouteForSecureElement.putIfAbsent(name, route); 467 mSecureElementForRoute.putIfAbsent(route, name); 468 } 469 } 470 471 for (Map.Entry<String, Integer> entry : mRouteForSecureElement.entrySet()) { 472 Log.d(TAG, "addOrUpdateTableItems: route: " + entry.getKey() + ", nfceeId: " 473 + Integer.toHexString(entry.getValue())); 474 } 475 for (Map.Entry<Integer, String> entry : mSecureElementForRoute.entrySet()) { 476 Log.d(TAG, "addOrUpdateTableItems: nfceeId: " + Integer.toHexString(entry.getKey()) 477 + ", route: " + entry.getValue()); 478 } 479 } 480 } 481