1 /*
2  * Copyright (C) 2022 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 package com.android.server.uwb.multchip;
18 
19 import android.content.Context;
20 import android.util.Log;
21 
22 import com.android.uwb.ChipGroupInfo;
23 import com.android.uwb.ChipInfo;
24 import com.android.uwb.Coordinates;
25 import com.android.uwb.UwbChipConfig;
26 import com.android.uwb.XmlParser;
27 import com.android.uwb.resources.R;
28 
29 import com.google.common.base.Strings;
30 import com.google.uwb.support.multichip.ChipInfoParams;
31 
32 import org.xmlpull.v1.XmlPullParserException;
33 
34 import java.io.BufferedInputStream;
35 import java.io.FileInputStream;
36 import java.io.IOException;
37 import java.io.InputStream;
38 import java.util.ArrayList;
39 import java.util.List;
40 import java.util.stream.Collectors;
41 
42 import javax.xml.datatype.DatatypeConfigurationException;
43 
44 /**
45  * Manages UWB chip information (such as id and position) for a multi-chip device.
46  */
47 public class UwbMultichipData {
48     private static final String TAG = "UwbMultichipData";
49     private final Context mContext;
50     private String mDefaultChipId = "default";
51     private List<ChipInfoParams> mChipInfoParamsList =
52             List.of(ChipInfoParams.createBuilder().setChipId(mDefaultChipId).build());
53     private List<String> mChipIds = List.of(mDefaultChipId);
54     private OnInitializedListener mListener;
55 
UwbMultichipData(Context context)56     public UwbMultichipData(Context context) {
57         mContext = context;
58     }
59 
60     /**
61      * Reads in a configuration file to initialize chip info, if the device is a multi-chip system
62      * a configuration file is defined and available.
63      *
64      * <p>If the device is single-chip, or if no configuration file is available, default values are
65      * used.
66      */
initialize()67     public void initialize() {
68         if (mContext.getResources().getBoolean(R.bool.config_isMultichip)) {
69             String filePath =
70                     mContext.getResources().getString(R.string.config_multichipConfigPath);
71             if (Strings.isNullOrEmpty(filePath)) {
72                 Log.w(TAG, "Multichip is set to true, but configuration file is not defined.");
73             } else {
74                 readConfigurationFile(filePath);
75             }
76         }
77         if (mListener != null) {
78             mListener.onInitialized();
79         }
80     }
81 
82     /**
83      * Returns a list of UWB chip infos in a {@link ChipInfoParams} object.
84      *
85      * Callers can invoke methods on a specific UWB chip by passing its {@code chipId} to the
86      * method, which can be determined by calling:
87      * <pre>
88      * {@code
89      * List<ChipInfoParams> chipInfos = getChipInfos();
90      * for (ChipInfoParams chipInfo : chipInfos) {
91      *     String chipId = chipInfo.getChipId();
92      * }
93      * }
94      * </pre>
95      *
96      * @return list of {@link ChipInfoParams} containing info about UWB chips for a multi-HAL
97      * system, or a list of info for a single chip for a single HAL system.
98      */
getChipInfos()99     public List<ChipInfoParams> getChipInfos() {
100         return mChipInfoParamsList;
101     }
102 
103     /**
104      * Convenience method that returns a list of UWB chip ids.
105      *
106      * @return List of UWB chip ids
107      */
getChipIds()108     public List<String> getChipIds() {
109         return mChipIds;
110     }
111 
112     /**
113      * Returns the default UWB chip identifier.
114      *
115      * If callers do not pass a specific {@code chipId} to UWB methods, then the method will be
116      * invoked on the default chip, which is determined at system initialization from a
117      * configuration file.
118      *
119      * @return default UWB chip identifier for a multi-HAL system, or the identifier of the only UWB
120      * chip in a single HAL system.
121      */
getDefaultChipId()122     public String getDefaultChipId() {
123         return mDefaultChipId;
124     }
125 
126     /**
127      * Sets an {@link OnInitializedListener}.
128      */
setOnInitializedListener(OnInitializedListener listener)129     public void setOnInitializedListener(OnInitializedListener listener) {
130         mListener = listener;
131     }
132 
readConfigurationFile(String filePath)133     private void readConfigurationFile(String filePath) {
134         InputStream stream = null;
135         try {
136             stream = new BufferedInputStream(new FileInputStream(filePath));
137             UwbChipConfig uwbChipConfig = XmlParser.read(stream);
138             mDefaultChipId = uwbChipConfig.getDefaultChipId();
139             Log.d(TAG, "Default chip id is " + mDefaultChipId);
140             // Reset mChipInfoParamsList so that it can be populated with values from configuration
141             // file.
142             mChipInfoParamsList = new ArrayList<>();
143             List<ChipGroupInfo> chipGroups = uwbChipConfig.getChipGroup();
144             for (ChipGroupInfo chipGroup : chipGroups) {
145                 List<ChipInfo> chips = chipGroup.getChip();
146                 for (ChipInfo chip : chips) {
147                     String chipId = chip.getId();
148                     Coordinates position = chip.getPosition();
149                     double x, y, z;
150                     if (position == null) {
151                         x = 0.0;
152                         y = 0.0;
153                         z = 0.0;
154                     } else {
155                         x = position.getX() == null ? 0.0 : position.getX().doubleValue();
156                         y = position.getY() == null ? 0.0 : position.getY().doubleValue();
157                         z = position.getZ() == null ? 0.0 : position.getZ().doubleValue();
158                     }
159                     Log.d(TAG,
160                             "Chip with id " + chipId + " has position " + x + ", " + y + ", " + z);
161                     mChipInfoParamsList
162                             .add(ChipInfoParams.createBuilder()
163                                     .setChipId(chipId)
164                                     .setPositionX(x)
165                                     .setPositionY(y)
166                                     .setPositionZ(z).build());
167                 }
168             }
169             mChipIds = mChipInfoParamsList.stream().map(ChipInfoParams::getChipId).collect(
170                     Collectors.toUnmodifiableList());
171         } catch (XmlPullParserException | IOException | DatatypeConfigurationException e) {
172             Log.e(TAG, "Cannot read file " + filePath, e);
173         } finally {
174             try {
175                 if (stream != null)
176                     stream.close();
177             } catch (IOException e) {
178                 Log.e(TAG, "Cannot close file " + filePath, e);
179             }
180         }
181     }
182 
183     /**
184      * Listener for initialization of {@link UwbMultichipData}.
185      */
186     public interface OnInitializedListener {
187         /**
188          * Invoked by {@link UwbMultichipData#initialize()}.
189          */
onInitialized()190         void onInitialized();
191     }
192 }
193