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