• 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/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