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.internal.telephony.uicc; 18 19 import android.annotation.NonNull; 20 import android.content.Context; 21 import android.os.IBinder; 22 import android.os.Message; 23 import android.os.RemoteException; 24 import android.telephony.SubscriptionInfo; 25 import android.util.IndentingPrintWriter; 26 27 import com.android.internal.annotations.GuardedBy; 28 import com.android.internal.annotations.VisibleForTesting; 29 import com.android.internal.telephony.CommandsInterface; 30 import com.android.internal.telephony.IccLogicalChannelRequest; 31 import com.android.internal.telephony.TelephonyComponentFactory; 32 import com.android.internal.telephony.flags.FeatureFlags; 33 import com.android.internal.telephony.flags.FeatureFlagsImpl; 34 import com.android.telephony.Rlog; 35 36 import dalvik.system.CloseGuard; 37 38 import java.io.FileDescriptor; 39 import java.io.PrintWriter; 40 import java.util.ArrayList; 41 import java.util.List; 42 43 public class UiccPort { 44 protected static final String LOG_TAG = "UiccPort"; 45 protected static final boolean DBG = true; 46 private static @NonNull FeatureFlags sFlags = new FeatureFlagsImpl(); 47 48 // The lock object is created by UiccSlot that owns this UiccCard - this is to share the lock 49 // between UiccSlot, UiccCard, EuiccCard, UiccPort, EuiccPort and UiccProfile for now. 50 protected final Object mLock; 51 private final CloseGuard mCloseGuard = CloseGuard.get(); 52 53 private String mIccid; 54 protected String mCardId; 55 private Context mContext; 56 private CommandsInterface mCi; 57 private UiccProfile mUiccProfile; 58 59 private final int mPhoneId; 60 private int mPortIdx; 61 private int mPhysicalSlotIndex; 62 63 // The list of the opened logical channel record. 64 // The channels will be closed by us when detecting client died without closing them in advance. 65 // The same lock should be used to protect both access of the list and the individual record. 66 @GuardedBy("mOpenChannelRecords") 67 private final List<OpenLogicalChannelRecord> mOpenChannelRecords = new ArrayList<>(); 68 UiccPort(Context c, CommandsInterface ci, IccCardStatus ics, int phoneId, Object lock, UiccCard uiccCard)69 public UiccPort(Context c, CommandsInterface ci, IccCardStatus ics, int phoneId, Object lock, 70 UiccCard uiccCard) { 71 if (DBG) log("Creating"); 72 mPhoneId = phoneId; 73 mLock = lock; 74 mCloseGuard.open("cleanup"); 75 update(c, ci, ics, uiccCard); 76 } 77 78 /** 79 * Update port. The main trigger for this is a change in the ICC Card status. 80 */ update(Context c, CommandsInterface ci, IccCardStatus ics, UiccCard uiccCard)81 public void update(Context c, CommandsInterface ci, IccCardStatus ics, UiccCard uiccCard) { 82 synchronized (mLock) { 83 mContext = c; 84 mCi = ci; 85 mIccid = ics.iccid; 86 mPortIdx = ics.mSlotPortMapping.mPortIndex; 87 mPhysicalSlotIndex = ics.mSlotPortMapping.mPhysicalSlotIndex; 88 if (mUiccProfile == null) { 89 mUiccProfile = TelephonyComponentFactory.getInstance() 90 .inject(UiccProfile.class.getName()).makeUiccProfile( 91 mContext, mCi, ics, mPhoneId, uiccCard, mLock, sFlags); 92 } else { 93 mUiccProfile.update(mContext, mCi, ics); 94 } 95 } 96 } 97 98 /** 99 * Dispose the port and its related Uicc profiles. 100 */ dispose()101 public void dispose() { 102 synchronized (mLock) { 103 if (DBG) log("Disposing Port"); 104 mCloseGuard.close(); 105 if (mUiccProfile != null) { 106 mUiccProfile.dispose(); 107 } 108 mUiccProfile = null; 109 } 110 cleanupOpenLogicalChannelRecordsIfNeeded(); 111 } 112 113 @Override finalize()114 protected void finalize() throws Throwable { 115 if (DBG) log("UiccPort finalized"); 116 try { 117 if (mCloseGuard != null) mCloseGuard.warnIfOpen(); 118 cleanupOpenLogicalChannelRecordsIfNeeded(); 119 } finally { 120 super.finalize(); 121 } 122 } 123 124 /** 125 * @deprecated Please use 126 * {@link UiccProfile#isApplicationOnIcc(IccCardApplicationStatus.AppType)} instead. 127 */ 128 @Deprecated isApplicationOnIcc(IccCardApplicationStatus.AppType type)129 public boolean isApplicationOnIcc(IccCardApplicationStatus.AppType type) { 130 synchronized (mLock) { 131 if (mUiccProfile != null) { 132 return mUiccProfile.isApplicationOnIcc(type); 133 } else { 134 return false; 135 } 136 } 137 } 138 139 /** 140 * @deprecated Please use {@link UiccProfile#getUniversalPinState()} instead. 141 */ 142 @Deprecated getUniversalPinState()143 public IccCardStatus.PinState getUniversalPinState() { 144 synchronized (mLock) { 145 if (mUiccProfile != null) { 146 return mUiccProfile.getUniversalPinState(); 147 } else { 148 return IccCardStatus.PinState.PINSTATE_UNKNOWN; 149 } 150 } 151 } 152 153 /** 154 * @deprecated Please use {@link UiccProfile#getApplication(int)} instead. 155 */ 156 @Deprecated getApplication(int family)157 public UiccCardApplication getApplication(int family) { 158 synchronized (mLock) { 159 if (mUiccProfile != null) { 160 return mUiccProfile.getApplication(family); 161 } else { 162 return null; 163 } 164 } 165 } 166 167 /** 168 * @deprecated Please use {@link UiccProfile#getApplicationIndex(int)} instead. 169 */ 170 @Deprecated getApplicationIndex(int index)171 public UiccCardApplication getApplicationIndex(int index) { 172 synchronized (mLock) { 173 if (mUiccProfile != null) { 174 return mUiccProfile.getApplicationIndex(index); 175 } else { 176 return null; 177 } 178 } 179 } 180 181 /** 182 * Returns the SIM application of the specified type. 183 * 184 * @param type ICC application type 185 * (@see com.android.internal.telephony.PhoneConstants#APPTYPE_xxx) 186 * @return application corresponding to type or a null if no match found 187 * 188 * @deprecated Please use {@link UiccProfile#getApplicationByType(int)} instead. 189 */ 190 @Deprecated getApplicationByType(int type)191 public UiccCardApplication getApplicationByType(int type) { 192 synchronized (mLock) { 193 if (mUiccProfile != null) { 194 return mUiccProfile.getApplicationByType(type); 195 } else { 196 return null; 197 } 198 } 199 } 200 201 /** 202 * Resets the application with the input AID. Returns true if any changes were made. 203 * 204 * A null aid implies a card level reset - all applications must be reset. 205 * 206 * @deprecated Please use {@link UiccProfile#resetAppWithAid(String, boolean)} instead. 207 */ 208 @Deprecated resetAppWithAid(String aid, boolean reset)209 public boolean resetAppWithAid(String aid, boolean reset) { 210 synchronized (mLock) { 211 if (mUiccProfile != null) { 212 return mUiccProfile.resetAppWithAid(aid, reset); 213 } else { 214 return false; 215 } 216 } 217 } 218 219 /** 220 * Exposes {@link CommandsInterface#iccOpenLogicalChannel} 221 * @deprecated Please use 222 * {@link UiccProfile#iccOpenLogicalChannel(String, int, Message)} instead. 223 */ 224 @Deprecated iccOpenLogicalChannel(String AID, int p2, Message response)225 public void iccOpenLogicalChannel(String AID, int p2, Message response) { 226 if (mUiccProfile != null) { 227 mUiccProfile.iccOpenLogicalChannel(AID, p2, response); 228 } else { 229 loge("iccOpenLogicalChannel Failed!"); 230 } 231 } 232 233 /** 234 * Exposes {@link CommandsInterface#iccCloseLogicalChannel} 235 * @deprecated Please use 236 * {@link UiccProfile#iccCloseLogicalChannel(int, boolean, Message)} instead. 237 */ 238 @Deprecated iccCloseLogicalChannel(int channel, Message response)239 public void iccCloseLogicalChannel(int channel, Message response) { 240 if (mUiccProfile != null) { 241 mUiccProfile.iccCloseLogicalChannel(channel, false /*isEs10*/, response); 242 } else { 243 loge("iccCloseLogicalChannel Failed!"); 244 } 245 } 246 247 /** 248 * Exposes {@link CommandsInterface#iccTransmitApduLogicalChannel} 249 * @deprecated Please use {@link 250 * UiccProfile#iccTransmitApduLogicalChannel(int, int, int, int, int, int, String, 251 * boolean, Message)} instead. 252 */ 253 @Deprecated iccTransmitApduLogicalChannel(int channel, int cla, int command, int p1, int p2, int p3, String data, Message response)254 public void iccTransmitApduLogicalChannel(int channel, int cla, int command, 255 int p1, int p2, int p3, String data, Message response) { 256 if (mUiccProfile != null) { 257 mUiccProfile.iccTransmitApduLogicalChannel(channel, cla, command, p1, p2, p3, 258 data, false /*isEs10Command*/, response); 259 } else { 260 loge("iccTransmitApduLogicalChannel Failed!"); 261 } 262 } 263 264 /** 265 * Exposes {@link CommandsInterface#iccTransmitApduBasicChannel} 266 * @deprecated Please use 267 * {@link UiccProfile#iccTransmitApduBasicChannel(int, int, int, int, int, String, Message)} 268 * instead. 269 */ 270 @Deprecated iccTransmitApduBasicChannel(int cla, int command, int p1, int p2, int p3, String data, Message response)271 public void iccTransmitApduBasicChannel(int cla, int command, 272 int p1, int p2, int p3, String data, Message response) { 273 if (mUiccProfile != null) { 274 mUiccProfile.iccTransmitApduBasicChannel(cla, command, p1, p2, p3, data, response); 275 } else { 276 loge("iccTransmitApduBasicChannel Failed!"); 277 } 278 } 279 280 /** 281 * Exposes {@link CommandsInterface#iccIO} 282 * @deprecated Please use 283 * {@link UiccProfile#iccExchangeSimIO(int, int, int, int, int, String, Message)} instead. 284 */ 285 @Deprecated iccExchangeSimIO(int fileID, int command, int p1, int p2, int p3, String pathID, Message response)286 public void iccExchangeSimIO(int fileID, int command, int p1, int p2, int p3, 287 String pathID, Message response) { 288 if (mUiccProfile != null) { 289 mUiccProfile.iccExchangeSimIO(fileID, command, p1, p2, p3, pathID, response); 290 } else { 291 loge("iccExchangeSimIO Failed!"); 292 } 293 } 294 295 /** 296 * Exposes {@link CommandsInterface#sendEnvelopeWithStatus} 297 * @deprecated Please use {@link UiccProfile#sendEnvelopeWithStatus(String, Message)} instead. 298 */ 299 @Deprecated sendEnvelopeWithStatus(String contents, Message response)300 public void sendEnvelopeWithStatus(String contents, Message response) { 301 if (mUiccProfile != null) { 302 mUiccProfile.sendEnvelopeWithStatus(contents, response); 303 } else { 304 loge("sendEnvelopeWithStatus Failed!"); 305 } 306 } 307 308 /** 309 * Returns number of applications on this card 310 * @deprecated Please use {@link UiccProfile#getNumApplications()} instead. 311 */ 312 @Deprecated getNumApplications()313 public int getNumApplications() { 314 if (mUiccProfile != null) { 315 return mUiccProfile.getNumApplications(); 316 } else { 317 return 0; 318 } 319 } 320 getPhoneId()321 public int getPhoneId() { 322 return mPhoneId; 323 } 324 getPortIdx()325 public int getPortIdx() { 326 return mPortIdx; 327 } 328 getUiccProfile()329 public UiccProfile getUiccProfile() { 330 return mUiccProfile; 331 } 332 333 /** 334 * @deprecated Please use {@link UiccProfile#setOperatorBrandOverride(String)} instead. 335 */ 336 @Deprecated setOperatorBrandOverride(String brand)337 public boolean setOperatorBrandOverride(String brand) { 338 if (mUiccProfile != null) { 339 return mUiccProfile.setOperatorBrandOverride(brand); 340 } else { 341 return false; 342 } 343 } 344 345 /** 346 * @deprecated Please use {@link UiccProfile#getOperatorBrandOverride()} instead. 347 */ 348 @Deprecated getOperatorBrandOverride()349 public String getOperatorBrandOverride() { 350 if (mUiccProfile != null) { 351 return mUiccProfile.getOperatorBrandOverride(); 352 } else { 353 return null; 354 } 355 } 356 357 /** 358 * Return the IccId corresponding to the port. 359 */ getIccId()360 public String getIccId() { 361 if (mIccid != null) { 362 return mIccid; 363 } else if (mUiccProfile != null) { 364 return mUiccProfile.getIccId(); 365 } else { 366 return null; 367 } 368 } 369 log(String msg)370 private void log(String msg) { 371 Rlog.d(LOG_TAG, msg); 372 } 373 loge(String msg)374 private void loge(String msg) { 375 Rlog.e(LOG_TAG, msg); 376 } 377 dump(FileDescriptor fd, PrintWriter printWriter, String[] args)378 public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) { 379 IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " "); 380 pw.println("UiccPort:"); 381 pw.increaseIndent(); 382 pw.println("mPortIdx=" + mPortIdx); 383 pw.println("mCi=" + mCi); 384 pw.println("mIccid=" + SubscriptionInfo.getPrintableId(mIccid)); 385 pw.println("mPhoneId=" + mPhoneId); 386 pw.println("mPhysicalSlotIndex=" + mPhysicalSlotIndex); 387 synchronized (mOpenChannelRecords) { 388 pw.println("mOpenChannelRecords=" + mOpenChannelRecords); 389 } 390 pw.println("mUiccProfile"); 391 if (mUiccProfile != null) { 392 mUiccProfile.dump(fd, pw, args); 393 } 394 } 395 396 /** 397 * Informed that a logical channel has been successfully opened. 398 * 399 * @param request the original request to open the channel, with channel id attached. 400 * @hide 401 */ onLogicalChannelOpened(@onNull IccLogicalChannelRequest request)402 public void onLogicalChannelOpened(@NonNull IccLogicalChannelRequest request) { 403 OpenLogicalChannelRecord record = new OpenLogicalChannelRecord(request); 404 try { 405 synchronized (mOpenChannelRecords) { 406 request.binder.linkToDeath(record, /*flags=*/ 0); 407 mOpenChannelRecords.add(record); 408 } 409 if (DBG) log("onLogicalChannelOpened: monitoring client " + record); 410 } catch (RemoteException | NullPointerException ex) { 411 loge("IccOpenLogicChannel client has died, clean up manually"); 412 record.binderDied(); 413 } 414 } 415 416 /** 417 * Informed that a logical channel has been successfully closed. 418 * 419 * @param channelId the channel id of the logical channel that was just closed. 420 * @hide 421 */ onLogicalChannelClosed(int channelId)422 public void onLogicalChannelClosed(int channelId) { 423 OpenLogicalChannelRecord record = getOpenLogicalChannelRecord(channelId); 424 synchronized (mOpenChannelRecords) { 425 if (record != null && record.mRequest != null && record.mRequest.binder != null) { 426 if (DBG) log("onLogicalChannelClosed: stop monitoring client " + record); 427 record.mRequest.binder.unlinkToDeath(record, /*flags=*/ 0); 428 record.mRequest.binder = null; 429 mOpenChannelRecords.remove(record); 430 } 431 } 432 } 433 434 /** Get the OpenLogicalChannelRecord matching the channel id. */ 435 @VisibleForTesting getOpenLogicalChannelRecord(int channelId)436 public OpenLogicalChannelRecord getOpenLogicalChannelRecord(int channelId) { 437 synchronized (mOpenChannelRecords) { 438 for (OpenLogicalChannelRecord channelRecord : mOpenChannelRecords) { 439 if (channelRecord.mRequest != null 440 && channelRecord.mRequest.channel == channelId) { 441 return channelRecord; 442 } 443 } 444 } 445 return null; 446 } 447 448 /** 449 * Clean up records when logical channels underneath have been released, in cases like SIM 450 * removal or modem reset. The obsoleted records may trigger a redundant release of logical 451 * channel that may have been assigned to other client. 452 */ 453 @SuppressWarnings("GuardedBy") cleanupOpenLogicalChannelRecordsIfNeeded()454 private void cleanupOpenLogicalChannelRecordsIfNeeded() { 455 // This check may raise GuardedBy warning, but we need it as long as this method is called 456 // from finalize(). We can remove it from there once UiccPort is fully protected against 457 // resource leak (e.g. with CloseGuard) and all (direct and indirect) users are fixed. 458 if (mOpenChannelRecords == null) return; 459 460 synchronized (mOpenChannelRecords) { 461 for (OpenLogicalChannelRecord record : mOpenChannelRecords) { 462 if (DBG) log("Clean up " + record); 463 record.mRequest.binder.unlinkToDeath(record, /*flags=*/ 0); 464 record.mRequest.binder = null; 465 } 466 mOpenChannelRecords.clear(); 467 } 468 } 469 470 /** Record to keep open logical channel info. */ 471 @VisibleForTesting 472 public class OpenLogicalChannelRecord implements IBinder.DeathRecipient { 473 IccLogicalChannelRequest mRequest; 474 OpenLogicalChannelRecord(IccLogicalChannelRequest request)475 OpenLogicalChannelRecord(IccLogicalChannelRequest request) { 476 this.mRequest = request; 477 } 478 479 @Override binderDied()480 public void binderDied() { 481 loge("IccOpenLogicalChannelRecord: client died, close channel in record " + this); 482 iccCloseLogicalChannel(mRequest.channel, /* response= */ null); 483 onLogicalChannelClosed(mRequest.channel); 484 } 485 486 @Override toString()487 public String toString() { 488 StringBuilder sb = new StringBuilder("OpenLogicalChannelRecord {"); 489 sb.append(" mRequest=" + mRequest).append("}"); 490 return sb.toString(); 491 } 492 } 493 } 494