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 package com.google.android.car.kitchensink.vhal; 17 18 import android.annotation.Nullable; 19 import android.app.AlertDialog; 20 import android.content.Context; 21 import android.hardware.automotive.vehicle.V2_0.IVehicle; 22 import android.hardware.automotive.vehicle.V2_0.StatusCode; 23 import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig; 24 import android.hardware.automotive.vehicle.V2_0.VehiclePropValue; 25 import android.hardware.automotive.vehicle.V2_0.VehicleProperty; 26 import android.os.Bundle; 27 import android.os.RemoteException; 28 import android.util.Log; 29 import android.view.LayoutInflater; 30 import android.view.View; 31 import android.view.ViewGroup; 32 import android.widget.ArrayAdapter; 33 import android.widget.Button; 34 import android.widget.LinearLayout; 35 import android.widget.ListView; 36 import android.widget.TextView; 37 38 import androidx.fragment.app.Fragment; 39 40 import com.google.android.car.kitchensink.KitchenSinkActivity; 41 import com.google.android.car.kitchensink.R; 42 43 import java.util.List; 44 import java.util.Objects; 45 import java.util.stream.Collectors; 46 47 public class VehicleHalFragment extends Fragment { 48 49 private static final String TAG = "CAR.VEHICLEHAL.KS"; 50 51 private KitchenSinkActivity mActivity; 52 private ListView mListView; 53 54 @Nullable 55 @Override onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)56 public View onCreateView(LayoutInflater inflater, 57 @Nullable ViewGroup container, 58 @Nullable Bundle savedInstanceState) { 59 View view = inflater.inflate(R.layout.vhal, container, false); 60 mActivity = (KitchenSinkActivity) getHost(); 61 mListView = view.findViewById(R.id.hal_prop_list); 62 return view; 63 } 64 65 @Override onResume()66 public void onResume() { 67 super.onResume(); 68 69 final boolean retry = true; 70 final IVehicle vehicle; 71 try { 72 vehicle = Objects.requireNonNull(IVehicle.getService(retry)); 73 } catch (RemoteException | RuntimeException e) { 74 Log.e(TAG, "unable to retrieve Vehicle HAL service", e); 75 AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); 76 builder.setTitle("Vehicle HAL not available") 77 .setPositiveButton(android.R.string.ok, (x, y) -> { }) 78 .setMessage("In some cases (e.g. SELinux enforcing mode), this UI " 79 + "is not available for use. Please use the car property UI") 80 .show(); 81 return; 82 } 83 84 final List<VehiclePropConfig> propConfigList; 85 try { 86 propConfigList = Objects.requireNonNull(vehicle.getAllPropConfigs()); 87 } catch (NullPointerException | RemoteException e) { 88 Log.e(TAG, "unable to retrieve prop configs", e); 89 return; 90 } 91 92 final List<HalPropertyInfo> supportedProperties = propConfigList.stream() 93 .map(HalPropertyInfo::new) 94 .sorted() 95 .collect(Collectors.toList()); 96 97 mListView.setAdapter(new ListAdapter(mActivity, vehicle, supportedProperties)); 98 } 99 100 private static class HalPropertyInfo implements Comparable<HalPropertyInfo> { 101 102 public final int id; 103 public final String name; 104 public final VehiclePropConfig config; 105 HalPropertyInfo(VehiclePropConfig config)106 HalPropertyInfo(VehiclePropConfig config) { 107 this.config = config; 108 id = config.prop; 109 name = VehicleProperty.toString(id); 110 } 111 112 @Override toString()113 public String toString() { 114 return name; 115 } 116 117 @Override equals(Object other)118 public boolean equals(Object other) { 119 return other instanceof HalPropertyInfo && ((HalPropertyInfo) other).id == id; 120 } 121 122 @Override hashCode()123 public int hashCode() { 124 return id; 125 } 126 127 @Override compareTo(HalPropertyInfo halPropertyInfo)128 public int compareTo(HalPropertyInfo halPropertyInfo) { 129 return name.compareTo(halPropertyInfo.name); 130 } 131 getValue(IVehicle vehicle)132 public String getValue(IVehicle vehicle) { 133 String result[] = new String[] {"<unknown>"}; 134 135 try { 136 VehiclePropValue request = new VehiclePropValue(); 137 // TODO: add zones support 138 request.prop = id; 139 140 // NB: this call is synchronous 141 vehicle.get(request, (status, propValue) -> { 142 if (status == StatusCode.OK) { 143 result[0] = propValue.value.toString(); 144 } 145 }); 146 } catch (android.os.RemoteException e) { 147 Log.e(TAG, "unable to read property " + name, e); 148 } 149 150 return result[0]; 151 } 152 } 153 154 private static final class ListAdapter extends ArrayAdapter<HalPropertyInfo> { 155 private static final int RESOURCE_ID = R.layout.vhal_listitem; 156 157 // cannot use superclass' LayoutInflater as it is private 158 private final LayoutInflater mLayoutInflater; 159 private final IVehicle mVehicle; 160 ListAdapter(Context context, IVehicle vehicle, List<HalPropertyInfo> properties)161 ListAdapter(Context context, IVehicle vehicle, List<HalPropertyInfo> properties) { 162 super(context, RESOURCE_ID, properties); 163 mVehicle = vehicle; 164 mLayoutInflater = LayoutInflater.from(context); 165 } 166 167 @Override getView(int position, View convertView, ViewGroup parent)168 public View getView(int position, View convertView, ViewGroup parent) { 169 final HalPropertyInfo item = getItem(position); 170 171 final LinearLayout viewLayout; 172 if (convertView != null && convertView instanceof LinearLayout) { 173 viewLayout = (LinearLayout)convertView; 174 } else { 175 // this is the value used by the superclass's view inflater 176 final boolean attachToRoot = false; 177 178 viewLayout = 179 (LinearLayout)mLayoutInflater.inflate(RESOURCE_ID, parent, attachToRoot); 180 } 181 182 TextView textString = viewLayout.findViewById(R.id.textString); 183 Button infoButton = viewLayout.findViewById(R.id.infoButton); 184 Button valueButton = viewLayout.findViewById(R.id.valueButton); 185 186 infoButton.setOnClickListener(btn -> { 187 AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); 188 builder.setTitle("Configuration for " + item.name) 189 .setPositiveButton(android.R.string.yes, (x, y) -> { }) 190 .setMessage(item.config.toString()) 191 .show(); 192 }); 193 194 valueButton.setOnClickListener(btn -> { 195 AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); 196 builder.setTitle("Value for " + item.name) 197 .setPositiveButton(android.R.string.yes, (x, y) -> { }) 198 .setMessage(item.getValue(mVehicle)) 199 .show(); 200 }); 201 202 textString.setText(item.toString()); 203 return viewLayout; 204 } 205 } 206 } 207