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 package com.android.server.location.contexthub;
18 
19 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
20 
21 import android.Manifest;
22 import android.content.Context;
23 import android.hardware.contexthub.V1_0.ContextHub;
24 import android.hardware.contexthub.V1_0.ContextHubMsg;
25 import android.hardware.contexthub.V1_0.HostEndPoint;
26 import android.hardware.contexthub.V1_0.Result;
27 import android.hardware.contexthub.V1_2.HubAppInfo;
28 import android.hardware.location.ContextHubInfo;
29 import android.hardware.location.ContextHubTransaction;
30 import android.hardware.location.NanoAppBinary;
31 import android.hardware.location.NanoAppMessage;
32 import android.hardware.location.NanoAppState;
33 import android.os.Binder;
34 import android.os.Build;
35 import android.util.Log;
36 
37 import java.util.ArrayList;
38 import java.util.Collection;
39 import java.util.HashMap;
40 import java.util.HashSet;
41 import java.util.List;
42 import java.util.Set;
43 
44 /**
45  * A class encapsulating helper functions used by the ContextHubService class
46  */
47 /* package */ class ContextHubServiceUtil {
48     private static final String TAG = "ContextHubServiceUtil";
49     private static final String HARDWARE_PERMISSION = Manifest.permission.LOCATION_HARDWARE;
50     private static final String CONTEXT_HUB_PERMISSION = Manifest.permission.ACCESS_CONTEXT_HUB;
51 
52     // A set of packages that have already been warned regarding the ACCESS_CONTEXT_HUB permission.
53     private static final Set<String> PERMISSION_WARNED_PACKAGES = new HashSet<String>();
54 
55     /**
56      * Creates a ConcurrentHashMap of the Context Hub ID to the ContextHubInfo object given an
57      * ArrayList of HIDL ContextHub objects.
58      *
59      * @param hubList the ContextHub ArrayList
60      * @return the HashMap object
61      */
62     /* package */
createContextHubInfoMap(List<ContextHub> hubList)63     static HashMap<Integer, ContextHubInfo> createContextHubInfoMap(List<ContextHub> hubList) {
64         HashMap<Integer, ContextHubInfo> contextHubIdToInfoMap = new HashMap<>();
65         for (ContextHub contextHub : hubList) {
66             contextHubIdToInfoMap.put(contextHub.hubId, new ContextHubInfo(contextHub));
67         }
68 
69         return contextHubIdToInfoMap;
70     }
71 
72     /**
73      * Copies a primitive byte array to a ArrayList<Byte>.
74      *
75      * @param inputArray  the primitive byte array
76      * @param outputArray the ArrayList<Byte> array to append
77      */
78     /* package */
copyToByteArrayList(byte[] inputArray, ArrayList<Byte> outputArray)79     static void copyToByteArrayList(byte[] inputArray, ArrayList<Byte> outputArray) {
80         outputArray.clear();
81         outputArray.ensureCapacity(inputArray.length);
82         for (byte element : inputArray) {
83             outputArray.add(element);
84         }
85     }
86 
87     /**
88      * Creates a byte array given a ArrayList<Byte> and copies its contents.
89      *
90      * @param array the ArrayList<Byte> object
91      * @return the byte array
92      */
93     /* package */
createPrimitiveByteArray(ArrayList<Byte> array)94     static byte[] createPrimitiveByteArray(ArrayList<Byte> array) {
95         byte[] primitiveArray = new byte[array.size()];
96         for (int i = 0; i < array.size(); i++) {
97             primitiveArray[i] = array.get(i);
98         }
99 
100         return primitiveArray;
101     }
102 
103     /**
104      * Creates a primitive integer array given a Collection<Integer>.
105      *
106      * @param collection the collection to iterate
107      * @return the primitive integer array
108      */
createPrimitiveIntArray(Collection<Integer> collection)109     static int[] createPrimitiveIntArray(Collection<Integer> collection) {
110         int[] primitiveArray = new int[collection.size()];
111 
112         int i = 0;
113         for (int contextHubId : collection) {
114             primitiveArray[i++] = contextHubId;
115         }
116 
117         return primitiveArray;
118     }
119 
120     /**
121      * Generates the Context Hub HAL's NanoAppBinary object from the client-facing
122      * android.hardware.location.NanoAppBinary object.
123      *
124      * @param nanoAppBinary the client-facing NanoAppBinary object
125      * @return the Context Hub HAL's NanoAppBinary object
126      */
127     /* package */
createHidlNanoAppBinary( NanoAppBinary nanoAppBinary)128     static android.hardware.contexthub.V1_0.NanoAppBinary createHidlNanoAppBinary(
129             NanoAppBinary nanoAppBinary) {
130         android.hardware.contexthub.V1_0.NanoAppBinary hidlNanoAppBinary =
131                 new android.hardware.contexthub.V1_0.NanoAppBinary();
132 
133         hidlNanoAppBinary.appId = nanoAppBinary.getNanoAppId();
134         hidlNanoAppBinary.appVersion = nanoAppBinary.getNanoAppVersion();
135         hidlNanoAppBinary.flags = nanoAppBinary.getFlags();
136         hidlNanoAppBinary.targetChreApiMajorVersion = nanoAppBinary.getTargetChreApiMajorVersion();
137         hidlNanoAppBinary.targetChreApiMinorVersion = nanoAppBinary.getTargetChreApiMinorVersion();
138 
139         // Log exceptions while processing the binary, but continue to pass down the binary
140         // since the error checking is deferred to the Context Hub.
141         try {
142             copyToByteArrayList(nanoAppBinary.getBinaryNoHeader(), hidlNanoAppBinary.customBinary);
143         } catch (IndexOutOfBoundsException e) {
144             Log.w(TAG, e.getMessage());
145         } catch (NullPointerException e) {
146             Log.w(TAG, "NanoApp binary was null");
147         }
148 
149         return hidlNanoAppBinary;
150     }
151 
152     /**
153      * Generates a client-facing NanoAppState array from a HAL HubAppInfo array.
154      *
155      * @param nanoAppInfoList the array of HubAppInfo objects
156      * @return the corresponding array of NanoAppState objects
157      */
158     /* package */
createNanoAppStateList( List<HubAppInfo> nanoAppInfoList)159     static List<NanoAppState> createNanoAppStateList(
160             List<HubAppInfo> nanoAppInfoList) {
161         ArrayList<NanoAppState> nanoAppStateList = new ArrayList<>();
162         for (HubAppInfo appInfo : nanoAppInfoList) {
163             nanoAppStateList.add(
164                     new NanoAppState(appInfo.info_1_0.appId, appInfo.info_1_0.version,
165                                      appInfo.info_1_0.enabled, appInfo.permissions));
166         }
167 
168         return nanoAppStateList;
169     }
170 
171     /**
172      * Creates a HIDL ContextHubMsg object to send to a nanoapp.
173      *
174      * @param hostEndPoint the ID of the client sending the message
175      * @param message      the client-facing NanoAppMessage object describing the message
176      * @return the HIDL ContextHubMsg object
177      */
178     /* package */
createHidlContextHubMessage(short hostEndPoint, NanoAppMessage message)179     static ContextHubMsg createHidlContextHubMessage(short hostEndPoint, NanoAppMessage message) {
180         ContextHubMsg hidlMessage = new ContextHubMsg();
181 
182         hidlMessage.appName = message.getNanoAppId();
183         hidlMessage.hostEndPoint = hostEndPoint;
184         hidlMessage.msgType = message.getMessageType();
185         copyToByteArrayList(message.getMessageBody(), hidlMessage.msg);
186 
187         return hidlMessage;
188     }
189 
190     /**
191      * Creates a client-facing NanoAppMessage object to send to a client.
192      *
193      * @param message the HIDL ContextHubMsg object from a nanoapp
194      * @return the NanoAppMessage object
195      */
196     /* package */
createNanoAppMessage(ContextHubMsg message)197     static NanoAppMessage createNanoAppMessage(ContextHubMsg message) {
198         byte[] messageArray = createPrimitiveByteArray(message.msg);
199 
200         return NanoAppMessage.createMessageFromNanoApp(
201                 message.appName, message.msgType, messageArray,
202                 message.hostEndPoint == HostEndPoint.BROADCAST);
203     }
204 
205     /**
206      * Checks for location hardware permissions.
207      *
208      * @param context the context of the service
209      */
210     /* package */
checkPermissions(Context context)211     static void checkPermissions(Context context) {
212         boolean hasLocationHardwarePermission = (context.checkCallingPermission(HARDWARE_PERMISSION)
213                 == PERMISSION_GRANTED);
214         boolean hasAccessContextHubPermission = (context.checkCallingPermission(
215                 CONTEXT_HUB_PERMISSION) == PERMISSION_GRANTED);
216 
217         if (!hasLocationHardwarePermission && !hasAccessContextHubPermission) {
218             throw new SecurityException(
219                     "LOCATION_HARDWARE or ACCESS_CONTEXT_HUB permission required to use Context "
220                             + "Hub");
221         }
222 
223         if (!hasAccessContextHubPermission && !Build.IS_USER) {
224             String pkgName = context.getPackageManager().getNameForUid(Binder.getCallingUid());
225             if (!PERMISSION_WARNED_PACKAGES.contains(pkgName)) {
226                 Log.w(TAG, pkgName
227                         + ": please use the ACCESS_CONTEXT_HUB permission rather than "
228                         + "LOCATION_HARDWARE (will be removed for Context Hub APIs in T)");
229                 PERMISSION_WARNED_PACKAGES.add(pkgName);
230             }
231         }
232     }
233 
234     /**
235      * Helper function to convert from the HAL Result enum error code to the
236      * ContextHubTransaction.Result type.
237      *
238      * @param halResult the Result enum error code
239      * @return the ContextHubTransaction.Result equivalent
240      */
241     @ContextHubTransaction.Result
242     /* package */
toTransactionResult(int halResult)243     static int toTransactionResult(int halResult) {
244         switch (halResult) {
245             case Result.OK:
246                 return ContextHubTransaction.RESULT_SUCCESS;
247             case Result.BAD_PARAMS:
248                 return ContextHubTransaction.RESULT_FAILED_BAD_PARAMS;
249             case Result.NOT_INIT:
250                 return ContextHubTransaction.RESULT_FAILED_UNINITIALIZED;
251             case Result.TRANSACTION_PENDING:
252                 return ContextHubTransaction.RESULT_FAILED_BUSY;
253             case Result.TRANSACTION_FAILED:
254             case Result.UNKNOWN_FAILURE:
255             default: /* fall through */
256                 return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
257         }
258     }
259 
260     /**
261      * Converts old list of HubAppInfo received from the HAL to V1.2 HubAppInfo objects.
262      *
263      * @param oldInfoList list of V1.0 HubAppInfo objects
264      * @return list of V1.2 HubAppInfo objects
265      */
266     /* package */
toHubAppInfo_1_2( ArrayList<android.hardware.contexthub.V1_0.HubAppInfo> oldInfoList)267     static ArrayList<HubAppInfo> toHubAppInfo_1_2(
268             ArrayList<android.hardware.contexthub.V1_0.HubAppInfo> oldInfoList) {
269         ArrayList newAppInfo = new ArrayList<HubAppInfo>();
270         for (android.hardware.contexthub.V1_0.HubAppInfo oldInfo : oldInfoList) {
271             HubAppInfo newInfo = new HubAppInfo();
272             newInfo.info_1_0.appId = oldInfo.appId;
273             newInfo.info_1_0.version = oldInfo.version;
274             newInfo.info_1_0.memUsage = oldInfo.memUsage;
275             newInfo.info_1_0.enabled = oldInfo.enabled;
276             newInfo.permissions = new ArrayList<String>();
277             newAppInfo.add(newInfo);
278         }
279         return newAppInfo;
280     }
281 }
282