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/AppManager.h>
18
19 #include <aidl/android/hardware/radio/RadioConst.h>
20 #include <android-base/logging.h>
21 #include <libminradio/binder_printing.h>
22 #include <libminradio/sim/IccConstants.h>
23 #include <libminradio/sim/IccUtils.h>
24 #include <libminradio/sim/apps/FilesystemApp.h>
25
26 #include <set>
27
28 namespace android::hardware::radio::minimal::sim {
29
30 using namespace ::android::hardware::radio::minimal::binder_printing;
31 using namespace ::android::hardware::radio::minimal::sim::constants;
32 using ::aidl::android::hardware::radio::RadioConst;
33 using ::aidl::android::hardware::radio::RadioError;
34 namespace aidl = ::aidl::android::hardware::radio::sim;
35
36 // ETSI TS 102 221 10.1.2 (table 10.5)
37 static std::map<uint8_t, std::set<uint8_t>> mCommandClasses = {
38 {COMMAND_READ_BINARY, {0}}, //
39 {COMMAND_UPDATE_BINARY, {0}}, //
40 {COMMAND_READ_RECORD, {0}}, //
41 {COMMAND_UPDATE_RECORD, {0}}, //
42 {COMMAND_SEEK, {0}}, //
43 {COMMAND_SELECT, {0}}, //
44 {COMMAND_GET_RESPONSE, {0}}, //
45 {COMMAND_STATUS, {0x80, 0x81, 0x82, 0x83}}, //
46 {COMMAND_GET_DATA, {0x80}}, //
47 {COMMAND_MANAGE_CHANNEL, {0}}, //
48 };
49
50 static constexpr uint8_t MANAGE_CHANNEL_OPEN = 0x00;
51 static constexpr uint8_t MANAGE_CHANNEL_CLOSE = 0x80;
52
AppManager()53 AppManager::AppManager() {}
54
addApp(std::shared_ptr<App> app)55 void AppManager::addApp(std::shared_ptr<App> app) {
56 mApps[std::string{app->getAid()}] = app;
57
58 // Channel 0 is always available per 3GPP TS 102 221 11.1.17
59 if (app->getAid() == apps::FilesystemApp::AID) {
60 std::unique_lock lck(mChannelsGuard);
61 mChannels[0] = app->newChannel(0);
62 }
63 }
64
openLogicalChannel(std::string_view aid,int32_t p2)65 std::pair<RadioError, std::shared_ptr<App::Channel>> AppManager::openLogicalChannel(
66 std::string_view aid, int32_t p2) {
67 auto appIt = mApps.find(aid);
68 if (appIt == mApps.end()) {
69 LOG(WARNING) << "App " << aid << " not found";
70 return {RadioError::NO_SUCH_ELEMENT, nullptr};
71 }
72
73 // ETSI TS 102 221 11.1.1.2 Table 11.2
74 // P2 == 0x00: Application activation / reset; First or only occurrence
75 // 0x0C: No data returned
76 if (p2 != 0x00 && p2 != 0x0C && p2 != RadioConst::P2_CONSTANT_NO_P2) {
77 LOG(ERROR) << "P2 != 0x00 or 0x0C not supported";
78 return {RadioError::INVALID_ARGUMENTS, nullptr};
79 }
80
81 std::unique_lock lck(mChannelsGuard);
82
83 // Find available channel. It must be in 1-3 range per 3GPP TS 102 221 11.1.17.1
84 std::optional<unsigned> channelId;
85 for (uint8_t i = 1; i <= 3; i++) {
86 if (mChannels.find(i) == mChannels.end()) {
87 channelId = i;
88 break;
89 }
90 }
91 if (!channelId.has_value()) {
92 LOG(ERROR) << "AppManager: All channels are busy";
93 return {RadioError::MISSING_RESOURCE, nullptr};
94 }
95
96 auto channel = appIt->second->newChannel(*channelId);
97 mChannels[*channelId] = channel;
98 LOG(DEBUG) << "AppManager: opened logical channel " << *channelId;
99 return {RadioError::NONE, std::move(channel)};
100 }
101
closeLogicalChannel(int32_t channelId)102 RadioError AppManager::closeLogicalChannel(int32_t channelId) {
103 if (channelId == 0) {
104 // 3GPP TS 102 221 11.1.17: channel 0 is guaranteed to be always available
105 return RadioError::INVALID_ARGUMENTS;
106 }
107
108 std::unique_lock lck(mChannelsGuard);
109 auto it = mChannels.find(channelId);
110 if (it == mChannels.end()) {
111 return RadioError::MISSING_RESOURCE;
112 }
113 mChannels.erase(it);
114 LOG(DEBUG) << "AppManager: closed logical channel " << channelId;
115 return RadioError::NONE;
116 }
117
transmit(const aidl::SimApdu & message)118 aidl::IccIoResult AppManager::transmit(const aidl::SimApdu& message) {
119 // Fetch channel
120 std::shared_ptr<App::Channel> channel;
121 {
122 std::unique_lock lck(mChannelsGuard);
123 auto chIt = mChannels.find(message.sessionId);
124 if (chIt == mChannels.end()) {
125 return toIccIoResult(IO_RESULT_CHANNEL_NOT_SUPPORTED);
126 }
127 channel = chIt->second;
128 }
129
130 // Verify instruction matching command class
131 auto classIt = mCommandClasses.find(message.instruction);
132 if (classIt == mCommandClasses.end()) {
133 LOG(ERROR) << "Command not found for " << message;
134 return toIccIoResult(IO_RESULT_NOT_SUPPORTED);
135 }
136 if (!classIt->second.contains(message.cla)) {
137 LOG(ERROR) << "Unsupported command class: " << message;
138 return toIccIoResult(IO_RESULT_CLASS_NOT_SUPPORTED);
139 }
140
141 switch (message.instruction) {
142 case COMMAND_MANAGE_CHANNEL:
143 return commandManageChannel(message.p1, message.p2);
144 default:
145 // Pass the message to the channel
146 return channel->transmit(message);
147 }
148 }
149
iccIo(const aidl::IccIo & iccIo)150 aidl::IccIoResult AppManager::iccIo(const aidl::IccIo& iccIo) {
151 auto appIt = mApps.find(iccIo.aid);
152 if (appIt == mApps.end()) {
153 LOG(WARNING) << "App " << iccIo.aid << " not found";
154 return toIccIoResult(IO_RESULT_FILE_NOT_FOUND);
155 }
156
157 return appIt->second->iccIo(iccIo);
158 }
159
160 // ISO 7816 7.1.2
commandManageChannel(int32_t operation,int32_t channelId)161 aidl::IccIoResult AppManager::commandManageChannel(int32_t operation, int32_t channelId) {
162 if (operation == MANAGE_CHANNEL_OPEN) {
163 if (channelId != 0) {
164 LOG(ERROR) << "Not implemented: opening explicit channel IDs: " << channelId;
165 return toIccIoResult(IO_RESULT_INCORRECT_P1_P2);
166 }
167 auto [status, channel] = openLogicalChannel("", 0);
168 if (channel) {
169 return toIccIoResult(uint8ToBytes(channel->getId()));
170 } else {
171 return toIccIoResult(IO_RESULT_CHANNEL_NOT_SUPPORTED);
172 }
173 } else if (operation == MANAGE_CHANNEL_CLOSE) {
174 auto status = closeLogicalChannel(channelId);
175 if (status == RadioError::NONE) {
176 return toIccIoResult("");
177 }
178 return toIccIoResult(IO_RESULT_INCORRECT_P1_P2);
179 } else {
180 LOG(ERROR) << "Invalid MANAGE_CHANNEL operation: " << operation;
181 return toIccIoResult(IO_RESULT_INCORRECT_P1_P2);
182 }
183 }
184
185 } // namespace android::hardware::radio::minimal::sim
186