• 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 
17 #include <libminradio/sim/apps/FilesystemApp.h>
18 
19 #include "tlv.h"
20 
21 #include <android-base/logging.h>
22 #include <libminradio/binder_printing.h>
23 #include <libminradio/sim/IccConstants.h>
24 #include <libminradio/sim/IccUtils.h>
25 
26 #include <unordered_set>
27 
28 namespace android::hardware::radio::minimal::sim::apps {
29 
30 using namespace ::android::hardware::radio::minimal::binder_printing;
31 using namespace ::android::hardware::radio::minimal::sim::constants;
32 namespace aidl = ::aidl::android::hardware::radio::sim;
33 
34 // ETSI TS 102 221 11.1.1.2 Table 11.1: Coding of P1 for SELECT
35 static constexpr uint8_t SELECT_BY_FILE_ID = 0x00;
36 
37 // ETSI TS 102 221 11.1.1.2 Table 11.2: Coding of P2 for SELECT
38 static constexpr uint8_t SELECT_RETURN_FCP_TEMPLATE = 0x04;
39 static constexpr uint8_t SELECT_RETURN_NOTHING = 0x0C;
40 
41 // From android.carrierapi.cts.FcpTemplate
42 static constexpr uint8_t BER_TAG_FCP_TEMPLATE = 0x62;
43 static constexpr uint8_t FILE_IDENTIFIER = 0x83;
44 
45 static const std::unordered_set<int32_t> kLinearFixedFiles{EF_MSISDN};
46 
47 class FilesystemApp::FilesystemChannel : public App::Channel {
48   public:
49     FilesystemChannel(int32_t channelId, std::shared_ptr<Filesystem> filesystem);
50 
51     void select(Filesystem::Path path);
52     aidl::IccIoResult transmit(const aidl::SimApdu& message) override;
53 
54   private:
55     std::shared_ptr<Filesystem> mFilesystem;
56     Filesystem::Path mSelectedFile = paths::mf;
57 
58     aidl::IccIoResult commandSelect(int32_t p1, int32_t p2, int32_t p3, const std::string& data);
59     aidl::IccIoResult commandStatus(int32_t p1) const;
60     aidl::IccIoResult commandReadBinary(int32_t p1, int32_t p2) const;
61     aidl::IccIoResult commandUpdateBinary(int32_t p1, int32_t p2, std::string_view data);
62     aidl::IccIoResult commandReadRecord(int32_t p1, int32_t p2, int32_t p3);
63     aidl::IccIoResult commandGetResponse() const;
64 };
65 
FilesystemApp(const std::shared_ptr<Filesystem> & filesystem)66 FilesystemApp::FilesystemApp(const std::shared_ptr<Filesystem>& filesystem)
67     : App(AID), mFilesystem(filesystem) {}
68 
newChannel(int32_t id)69 std::shared_ptr<App::Channel> FilesystemApp::newChannel(int32_t id) {
70     auto channel = std::make_shared<FilesystemApp::FilesystemChannel>(id, mFilesystem);
71     if (id == 0) mBasicChannel = channel;
72     return channel;
73 }
74 
FilesystemChannel(int32_t channelId,std::shared_ptr<Filesystem> filesystem)75 FilesystemApp::FilesystemChannel::FilesystemChannel(  //
76         int32_t channelId, std::shared_ptr<Filesystem> filesystem)
77     : App::Channel(channelId), mFilesystem(filesystem) {}
78 
select(Filesystem::Path path)79 void FilesystemApp::FilesystemChannel::select(Filesystem::Path path) {
80     mSelectedFile = path;
81 }
82 
83 // android.carrierapi.cts.FcpTemplate.parseFcpTemplate (inversion)
makeFcpTemplate(const Filesystem::Path & path)84 static std::vector<uint8_t> makeFcpTemplate(const Filesystem::Path& path) {
85     // clang-format off
86     return makeTlv(BER_TAG_FCP_TEMPLATE,
87         makeTlv(FILE_IDENTIFIER, uint16ToBytes(path.fileId))
88     );
89     // clang-format on
90 }
91 
92 // ETSI TS 102 221 11.1.1
commandSelect(int32_t p1,int32_t p2,int32_t length,const std::string & data)93 aidl::IccIoResult FilesystemApp::FilesystemChannel::commandSelect(  //
94         int32_t p1, int32_t p2, int32_t length, const std::string& data) {
95     if (p1 != SELECT_BY_FILE_ID ||
96         (p2 != SELECT_RETURN_FCP_TEMPLATE && p2 != SELECT_RETURN_NOTHING)) {
97         return toIccIoResult(IO_RESULT_INCORRECT_P1_P2);
98     }
99     if (length != 2) {  // file ids are 2 byte long
100         return toIccIoResult(IO_RESULT_INCORRECT_LENGTH | 2);
101     }
102 
103     auto fileId = strtol(data.c_str(), nullptr, 16);
104     if (fileId <= 0 || fileId > 0xFFFF) {
105         LOG(WARNING) << "Incorrect file ID: " << data;
106         return toIccIoResult(IO_RESULT_INCORRECT_DATA);
107     }
108 
109     auto path = mFilesystem->find(fileId);
110     if (!path.has_value()) {
111         LOG(WARNING) << "FilesystemChannel: file " << std::hex << fileId << " not found";
112         return toIccIoResult(IO_RESULT_FILE_NOT_FOUND);
113     }
114     select(*path);
115 
116     if (p2 == SELECT_RETURN_FCP_TEMPLATE) {
117         return toIccIoResult(bytesToHexString(makeFcpTemplate(mSelectedFile)));
118     }
119     return toIccIoResult("");
120 }
121 
122 // ETSI TS 102 221 11.1.2
commandStatus(int32_t p1) const123 aidl::IccIoResult FilesystemApp::FilesystemChannel::commandStatus(int32_t p1) const {
124     if (p1 != 0x00 && p1 != 0x01) {  // 0x02 (termination) not implemented
125         return toIccIoResult(IO_RESULT_INCORRECT_P1_P2);
126     }
127     return toIccIoResult(bytesToHexString(makeFcpTemplate(mSelectedFile)));
128 }
129 
130 // ETSI TS 102 221 11.1.3
commandReadBinary(int32_t offsetHi,int32_t offsetLo) const131 aidl::IccIoResult FilesystemApp::FilesystemChannel::commandReadBinary(  //
132         int32_t offsetHi, int32_t offsetLo) const {
133     CHECK(offsetHi == 0 && offsetLo == 0) << "Offset not supported";
134     if (auto contents = mFilesystem->read(mSelectedFile); contents.has_value()) {
135         return toIccIoResult(*contents);
136     }
137     LOG(DEBUG) << "Missing ICC file (READ_BINARY): " << mSelectedFile.toString();
138     return toIccIoResult(IO_RESULT_FILE_NOT_FOUND);
139 }
140 
141 // ETSI TS 102 221 11.1.4
commandUpdateBinary(int32_t offsetHi,int32_t offsetLo,std::string_view data)142 aidl::IccIoResult FilesystemApp::FilesystemChannel::commandUpdateBinary(  //
143         int32_t offsetHi, int32_t offsetLo, std::string_view data) {
144     CHECK(offsetHi == 0 && offsetLo == 0) << "Offset not supported";
145     mFilesystem->write(mSelectedFile, hexStringToBytes(data));
146     return toIccIoResult("");
147 }
148 
149 // ETSI TS 102 221 11.1.5
commandReadRecord(int32_t recordId,int32_t mode,int32_t length)150 aidl::IccIoResult FilesystemApp::FilesystemChannel::commandReadRecord(  //
151         int32_t recordId, int32_t mode, int32_t length) {
152     CHECK(recordId == 1) << "Records other than no 1 are not supported";
153     CHECK(mode == 4) << "Unsupported record mode";  // absolute is the only currently supported mode
154     CHECK(length >= 0);
155     if (auto contents = mFilesystem->read(mSelectedFile); contents.has_value()) {
156         CHECK(static_cast<size_t>(length) == contents->size())
157                 << "Partial reads not supported (" << length << " != " << contents->size() << ")";
158         return toIccIoResult(*contents);
159     }
160     LOG(DEBUG) << "Missing ICC file (READ_RECORD): " << mSelectedFile.toString();
161     return toIccIoResult(IO_RESULT_FILE_NOT_FOUND);
162 }
163 
164 // com.android.internal.telephony.uicc.IccFileHandler (inversion)
165 // ETSI TS 102 221 12.1.1
commandGetResponse() const166 aidl::IccIoResult FilesystemApp::FilesystemChannel::commandGetResponse() const {
167     auto file = mSelectedFile;
168     auto contents = mFilesystem->read(file);
169     if (!contents.has_value()) {
170         LOG(DEBUG) << "Missing ICC file (GET_RESPONSE): " << file.toString();
171         return toIccIoResult(IO_RESULT_FILE_NOT_FOUND);
172     }
173     auto fileSize = contents->size();
174     CHECK(fileSize <= 0xFFFF) << "File size won't fit in GET_RESPONSE";
175 
176     // 3GPP TS 51.011 9.2.1
177     std::vector<uint8_t> response(GET_RESPONSE_EF_SIZE_BYTES, 0);
178     response[RESPONSE_DATA_FILE_SIZE_1] = fileSize >> 8;
179     response[RESPONSE_DATA_FILE_SIZE_2] = 0xFF & fileSize;
180     response[RESPONSE_DATA_FILE_ID_1] = file.fileId >> 8;
181     response[RESPONSE_DATA_FILE_ID_2] = 0xFF & file.fileId;
182     response[RESPONSE_DATA_FILE_TYPE] = TYPE_EF;
183     response[RESPONSE_DATA_LENGTH] = GET_RESPONSE_EF_SIZE_BYTES - RESPONSE_DATA_STRUCTURE;
184     if (kLinearFixedFiles.contains(file.fileId)) {
185         response[RESPONSE_DATA_STRUCTURE] = EF_TYPE_LINEAR_FIXED;
186         response[RESPONSE_DATA_RECORD_LENGTH] = fileSize;  // single record support only
187     } else {
188         response[RESPONSE_DATA_STRUCTURE] = EF_TYPE_TRANSPARENT;
189     }
190 
191     return toIccIoResult(response);
192 }
193 
transmit(const aidl::SimApdu & message)194 aidl::IccIoResult FilesystemApp::FilesystemChannel::transmit(const aidl::SimApdu& message) {
195     switch (message.instruction) {
196         case COMMAND_SELECT:
197             return commandSelect(message.p1, message.p2, message.p3, message.data);
198         case COMMAND_STATUS:
199             return commandStatus(message.p1);
200         case COMMAND_READ_BINARY:
201             return commandReadBinary(message.p1, message.p2);
202         case COMMAND_UPDATE_BINARY:
203             return commandUpdateBinary(message.p1, message.p2, message.data);
204         case COMMAND_READ_RECORD:
205             return commandReadRecord(message.p1, message.p2, message.p3);
206         case COMMAND_GET_RESPONSE:
207             return commandGetResponse();
208         default:
209             LOG(ERROR) << "Unsupported filesystem instruction: " << message;
210             return toIccIoResult(IO_RESULT_NOT_SUPPORTED);
211     }
212 }
213 
iccIo(const aidl::IccIo & iccIo)214 aidl::IccIoResult FilesystemApp::iccIo(const aidl::IccIo& iccIo) {
215     CHECK(mBasicChannel) << "Basic channel must always be present";
216 
217     if (iccIo.fileId != 0) {
218         mBasicChannel->select({iccIo.fileId, iccIo.path});
219     }
220 
221     aidl::SimApdu message = {
222             .instruction = iccIo.command,
223             .p1 = iccIo.p1,
224             .p2 = iccIo.p2,
225             .p3 = iccIo.p3,
226             .data = iccIo.data,
227     };
228     return mBasicChannel->transmit(message);
229 }
230 
231 }  // namespace android::hardware::radio::minimal::sim::apps
232