1 /*
2 * Copyright (C) 2020 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 //#define LOG_NDEBUG 0
18 #define LOG_TAG "MediaMetricsService::stringutils"
19 #include <utils/Log.h>
20
21 #include "StringUtils.h"
22
23 #include "AudioTypes.h"
24
25 namespace android::mediametrics::stringutils {
26
tokenizer(std::string::const_iterator & it,const std::string::const_iterator & end,const char * reserved)27 std::string tokenizer(std::string::const_iterator& it,
28 const std::string::const_iterator& end, const char *reserved)
29 {
30 // consume leading white space
31 for (; it != end && std::isspace(*it); ++it);
32 if (it == end) return {};
33
34 auto start = it;
35 // parse until we hit a reserved keyword or space
36 if (strchr(reserved, *it)) return {start, ++it};
37 for (;;) {
38 ++it;
39 if (it == end || std::isspace(*it) || strchr(reserved, *it)) return {start, it};
40 }
41 }
42
split(const std::string & flags,const char * delim)43 std::vector<std::string> split(const std::string& flags, const char *delim)
44 {
45 std::vector<std::string> result;
46 for (auto it = flags.begin(); ; ) {
47 auto flag = tokenizer(it, flags.end(), delim);
48 if (flag.empty() || !std::isalnum(flag[0])) return result;
49 result.emplace_back(std::move(flag));
50
51 // look for the delimeter and discard
52 auto token = tokenizer(it, flags.end(), delim);
53 if (token.size() != 1 || strchr(delim, token[0]) == nullptr) return result;
54 }
55 }
56
getDeviceAddressPairs(const std::string & devices)57 std::vector<std::pair<std::string, std::string>> getDeviceAddressPairs(const std::string& devices)
58 {
59 std::vector<std::pair<std::string, std::string>> result;
60
61 // Currently, the device format is EXACTLY
62 // (device1, addr1)|(device2, addr2)|...
63
64 static constexpr char delim[] = "()|,";
65 for (auto it = devices.begin(); ; ) {
66 auto token = tokenizer(it, devices.end(), delim);
67 if (token != "(") return result;
68
69 auto device = tokenizer(it, devices.end(), delim);
70 if (device.empty() || !std::isalnum(device[0])) return result;
71
72 token = tokenizer(it, devices.end(), delim);
73 if (token != ",") return result;
74
75 // special handling here for empty addresses
76 auto address = tokenizer(it, devices.end(), delim);
77 if (address.empty() || !std::isalnum(device[0])) return result;
78 if (address == ")") { // no address, just the ")"
79 address.clear();
80 } else {
81 token = tokenizer(it, devices.end(), delim);
82 if (token != ")") return result;
83 }
84
85 result.emplace_back(std::move(device), std::move(address));
86
87 token = tokenizer(it, devices.end(), delim);
88 if (token != "|") return result; // this includes end of string detection
89 }
90 }
91
replace(std::string & str,const char * targetChars,const char replaceChar)92 size_t replace(std::string &str, const char *targetChars, const char replaceChar)
93 {
94 size_t replaced = 0;
95 for (char &c : str) {
96 if (strchr(targetChars, c) != nullptr) {
97 c = replaceChar;
98 ++replaced;
99 }
100 }
101 return replaced;
102 }
103
104 template <types::AudioEnumCategory CATEGORY>
105 std::pair<std::string /* external statsd */, std::string /* internal */>
parseDevicePairs(const std::string & devicePairs)106 parseDevicePairs(const std::string& devicePairs) {
107 std::pair<std::string, std::string> result{};
108 const auto devaddrvec = stringutils::getDeviceAddressPairs(devicePairs);
109 for (const auto& [device, addr] : devaddrvec) { // addr ignored for now.
110 if (!result.second.empty()) {
111 result.second.append("|"); // delimit devices with '|'.
112 result.first.append("|");
113 }
114 result.second.append(device);
115 result.first.append(types::lookup<CATEGORY, std::string>(device));
116 }
117 return result;
118 }
119
120 std::pair<std::string /* external statsd */, std::string /* internal */>
parseOutputDevicePairs(const std::string & devicePairs)121 parseOutputDevicePairs(const std::string& devicePairs) {
122 return parseDevicePairs<types::OUTPUT_DEVICE>(devicePairs);
123 }
124
125 std::pair<std::string /* external statsd */, std::string /* internal */>
parseInputDevicePairs(const std::string & devicePairs)126 parseInputDevicePairs(const std::string& devicePairs) {
127 return parseDevicePairs<types::INPUT_DEVICE>(devicePairs);
128 }
129
130 } // namespace android::mediametrics::stringutils
131