Convert.java revision ca98cde254fef6c31634d8a3069a0d7b76ecf908
1/**
2 * Copyright (C) 2017 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
17package com.android.server.broadcastradio.hal2;
18
19import android.annotation.NonNull;
20import android.annotation.Nullable;
21import android.hardware.broadcastradio.V2_0.AmFmBandRange;
22import android.hardware.broadcastradio.V2_0.AmFmRegionConfig;
23import android.hardware.broadcastradio.V2_0.Properties;
24import android.hardware.broadcastradio.V2_0.Result;
25import android.hardware.broadcastradio.V2_0.VendorKeyValue;
26import android.hardware.radio.ProgramSelector;
27import android.hardware.radio.RadioManager;
28import android.os.ParcelableException;
29import android.util.Slog;
30
31import java.util.ArrayList;
32import java.util.Collections;
33import java.util.HashMap;
34import java.util.HashSet;
35import java.util.List;
36import java.util.Map;
37import java.util.Objects;
38import java.util.Set;
39
40class Convert {
41    private static final String TAG = "BcRadio2Srv.convert";
42
43    static void throwOnError(String action, int result) {
44        switch (result) {
45            case Result.OK:
46                return;
47            case Result.UNKNOWN_ERROR:
48                throw new ParcelableException(new RuntimeException(action + ": UNKNOWN_ERROR"));
49            case Result.INTERNAL_ERROR:
50                throw new ParcelableException(new RuntimeException(action + ": INTERNAL_ERROR"));
51            case Result.INVALID_ARGUMENTS:
52                throw new IllegalArgumentException(action + ": INVALID_ARGUMENTS");
53            case Result.INVALID_STATE:
54                throw new IllegalStateException(action + ": INVALID_STATE");
55            case Result.NOT_SUPPORTED:
56                throw new UnsupportedOperationException(action + ": NOT_SUPPORTED");
57            case Result.TIMEOUT:
58                throw new ParcelableException(new RuntimeException(action + ": TIMEOUT"));
59            default:
60                throw new ParcelableException(new RuntimeException(
61                        action + ": unknown error (" + result + ")"));
62        }
63    }
64
65    private static @NonNull Map<String, String>
66    vendorInfoFromHal(@Nullable List<VendorKeyValue> info) {
67        if (info == null) return Collections.emptyMap();
68
69        Map<String, String> map = new HashMap<>();
70        for (VendorKeyValue kvp : info) {
71            if (kvp.key == null || kvp.value == null) {
72                Slog.w(TAG, "VendorKeyValue contains null pointers");
73                continue;
74            }
75            map.put(kvp.key, kvp.value);
76        }
77
78        return map;
79    }
80
81    private static @NonNull int[]
82    identifierTypesToProgramTypes(@NonNull int[] idTypes) {
83        Set<Integer> pTypes = new HashSet<>();
84
85        for (int idType : idTypes) {
86            switch (idType) {
87                case ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY:
88                case ProgramSelector.IDENTIFIER_TYPE_RDS_PI:
89                    // TODO(b/69958423): verify AM/FM with region info
90                    pTypes.add(ProgramSelector.PROGRAM_TYPE_AM);
91                    pTypes.add(ProgramSelector.PROGRAM_TYPE_FM);
92                    break;
93                case ProgramSelector.IDENTIFIER_TYPE_HD_STATION_ID_EXT:
94                    // TODO(b/69958423): verify AM/FM with region info
95                    pTypes.add(ProgramSelector.PROGRAM_TYPE_AM_HD);
96                    pTypes.add(ProgramSelector.PROGRAM_TYPE_FM_HD);
97                    break;
98                case ProgramSelector.IDENTIFIER_TYPE_DAB_SIDECC:
99                case ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE:
100                case ProgramSelector.IDENTIFIER_TYPE_DAB_SCID:
101                case ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY:
102                    pTypes.add(ProgramSelector.PROGRAM_TYPE_DAB);
103                    break;
104                case ProgramSelector.IDENTIFIER_TYPE_DRMO_SERVICE_ID:
105                case ProgramSelector.IDENTIFIER_TYPE_DRMO_FREQUENCY:
106                    pTypes.add(ProgramSelector.PROGRAM_TYPE_DRMO);
107                    break;
108                case ProgramSelector.IDENTIFIER_TYPE_SXM_SERVICE_ID:
109                case ProgramSelector.IDENTIFIER_TYPE_SXM_CHANNEL:
110                    pTypes.add(ProgramSelector.PROGRAM_TYPE_SXM);
111                    break;
112                default:
113                    break;
114            }
115            if (idType >= ProgramSelector.IDENTIFIER_TYPE_VENDOR_PRIMARY_START
116                    && idType <= ProgramSelector.IDENTIFIER_TYPE_VENDOR_PRIMARY_END) {
117                pTypes.add(idType);
118            }
119        }
120
121        return pTypes.stream().mapToInt(Integer::intValue).toArray();
122    }
123
124    private static @NonNull RadioManager.BandDescriptor[]
125    amfmConfigToBands(@Nullable AmFmRegionConfig config) {
126        if (config == null) return new RadioManager.BandDescriptor[0];
127
128        int len = config.ranges.size();
129        List<RadioManager.BandDescriptor> bands = new ArrayList<>(len);
130
131        // Just a dummy value.
132        int region = RadioManager.REGION_ITU_1;
133
134        for (AmFmBandRange range : config.ranges) {
135            FrequencyBand bandType = Utils.getBand(range.lowerBound);
136            if (bandType == FrequencyBand.UNKNOWN) {
137                Slog.e(TAG, "Unknown frequency band at " + range.lowerBound + "kHz");
138                continue;
139            }
140            if (bandType == FrequencyBand.FM) {
141                bands.add(new RadioManager.FmBandDescriptor(region, RadioManager.BAND_FM,
142                    range.lowerBound, range.upperBound, range.spacing,
143
144                    // TODO(b/69958777): stereo, rds, ta, af, ea
145                    true, true, true, true, true
146                ));
147            } else {  // AM
148                bands.add(new RadioManager.AmBandDescriptor(region, RadioManager.BAND_AM,
149                    range.lowerBound, range.upperBound, range.spacing,
150
151                    // TODO(b/69958777): stereo
152                    true
153                ));
154            }
155        }
156
157        return bands.toArray(new RadioManager.BandDescriptor[bands.size()]);
158    }
159
160    static @NonNull RadioManager.ModuleProperties
161    propertiesFromHal(int id, @NonNull String serviceName, @NonNull Properties prop,
162            @Nullable AmFmRegionConfig amfmConfig) {
163        Objects.requireNonNull(serviceName);
164        Objects.requireNonNull(prop);
165
166        int[] supportedIdentifierTypes = prop.supportedIdentifierTypes.stream().
167                mapToInt(Integer::intValue).toArray();
168        int[] supportedProgramTypes = identifierTypesToProgramTypes(supportedIdentifierTypes);
169
170        return new RadioManager.ModuleProperties(
171                id,
172                serviceName,
173
174                // There is no Class concept in HAL 2.0.
175                RadioManager.CLASS_AM_FM,
176
177                prop.maker,
178                prop.product,
179                prop.version,
180                prop.serial,
181
182                /* HAL 2.0 only supports single tuner and audio source per
183                 * HAL implementation instance. */
184                1,      // numTuners
185                1,      // numAudioSources
186                false,  // isCaptureSupported
187
188                amfmConfigToBands(amfmConfig),
189                false,  // isBgScanSupported is deprecated
190                supportedProgramTypes,
191                supportedIdentifierTypes,
192                vendorInfoFromHal(prop.vendorInfo));
193    }
194}
195