1 /* 2 * Copyright (C) 2016 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.android.car.cluster; 17 18 import static com.android.car.cluster.InstrumentClusterRendererLoader.createRenderer; 19 import static com.android.car.cluster.InstrumentClusterRendererLoader.createRendererPackageContext; 20 21 import android.annotation.IntDef; 22 import android.annotation.Nullable; 23 import android.annotation.SystemApi; 24 import android.car.cluster.renderer.InstrumentClusterRenderer; 25 import android.car.cluster.renderer.NavigationRenderer; 26 import android.content.Context; 27 import android.hardware.display.DisplayManager; 28 import android.os.Handler; 29 import android.os.Looper; 30 import android.os.Message; 31 import android.util.Log; 32 import android.view.Display; 33 import android.view.LayoutInflater; 34 import android.view.View; 35 import android.view.ViewGroup; 36 37 import com.android.car.CarLog; 38 import com.android.car.CarServiceBase; 39 import com.android.car.CarServiceUtils; 40 import com.android.car.R; 41 42 import java.io.PrintWriter; 43 import java.lang.annotation.Retention; 44 import java.lang.annotation.RetentionPolicy; 45 import java.util.concurrent.CopyOnWriteArrayList; 46 47 /** 48 * Service responsible for interaction with car's instrument cluster. 49 * 50 * @hide 51 */ 52 @SystemApi 53 public class InstrumentClusterService implements CarServiceBase { 54 55 private static final String TAG = CarLog.TAG_CLUSTER + "." 56 + InstrumentClusterService.class.getSimpleName(); 57 58 private static final int RENDERER_INIT_TIMEOUT_MS = 10 * 1000; 59 private final static int MSG_TIMEOUT = 1; 60 61 private final Context mContext; 62 private final Object mHalSync = new Object(); 63 private final CopyOnWriteArrayList<RendererInitializationListener> 64 mRendererInitializationListeners = new CopyOnWriteArrayList<>(); 65 66 private InstrumentClusterRenderer mRenderer; 67 68 private final Handler mHandler; 69 InstrumentClusterService(Context context)70 public InstrumentClusterService(Context context) { 71 mContext = context; 72 mHandler = new TimeoutHandler(); 73 } 74 75 public interface RendererInitializationListener { onRendererInitSucceeded()76 void onRendererInitSucceeded(); 77 } 78 79 @Override init()80 public void init() { 81 Log.d(TAG, "init"); 82 83 if (getInstrumentClusterType() == InstrumentClusterType.GRAPHICS) { 84 Display display = getInstrumentClusterDisplay(mContext); 85 boolean rendererFound = InstrumentClusterRendererLoader.isRendererAvailable(mContext); 86 87 if (display != null && rendererFound) { 88 initRendererOnMainThread(display); 89 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_TIMEOUT), 90 RENDERER_INIT_TIMEOUT_MS); 91 } else { 92 mClusterType = InstrumentClusterType.NONE; 93 Log.w(TAG, "Failed to initialize InstrumentClusterRenderer" 94 + ", renderer found: " + rendererFound 95 + ", secondary display: " + (display != null), new RuntimeException()); 96 97 return; 98 } 99 } 100 } 101 initRendererOnMainThread(final Display display)102 private void initRendererOnMainThread(final Display display) { 103 CarServiceUtils.runOnMain(new Runnable() { 104 @Override 105 public void run() { 106 Log.d(TAG, "initRendererOnMainThread"); 107 try { 108 InstrumentClusterPresentation presentation = 109 new InstrumentClusterPresentation(mContext, display); 110 111 ViewGroup rootView = (ViewGroup) LayoutInflater.from(mContext).inflate( 112 R.layout.instrument_cluster, null); 113 114 presentation.setContentView(rootView); 115 InstrumentClusterRenderer renderer = createRenderer(mContext); 116 renderer.onCreate(createRendererPackageContext(mContext)); 117 View rendererView = renderer.onCreateView(null); 118 renderer.initialize(); 119 rootView.addView(rendererView); 120 presentation.show(); 121 renderer.onStart(); 122 initUiDone(renderer); 123 } catch (Exception e) { 124 Log.e(TAG, e.getMessage(), e); 125 throw e; 126 } 127 } 128 }); 129 } 130 initUiDone(final InstrumentClusterRenderer renderer)131 private void initUiDone(final InstrumentClusterRenderer renderer) { 132 Log.d(TAG, "initUiDone"); 133 mHandler.removeMessages(MSG_TIMEOUT); 134 135 // Call listeners in service thread. 136 runOnServiceThread(new Runnable() { 137 @Override 138 public void run() { 139 mRenderer = renderer; 140 141 for (RendererInitializationListener listener : mRendererInitializationListeners) { 142 listener.onRendererInitSucceeded(); 143 } 144 } 145 }); 146 } 147 148 @Override release()149 public void release() { 150 Log.d(TAG, "release"); 151 } 152 153 @Override dump(PrintWriter writer)154 public void dump(PrintWriter writer) { 155 // TODO 156 } 157 158 @Retention(RetentionPolicy.SOURCE) 159 @IntDef({ 160 InstrumentClusterType.NONE, 161 InstrumentClusterType.METADATA, 162 InstrumentClusterType.GRAPHICS 163 }) 164 public @interface InstrumentClusterType { 165 /** 166 * For use privately in this class. 167 * @hide 168 */ 169 int UNDEFINED = -1; 170 171 /** Access to instrument cluster is not available */ 172 int NONE = 0; 173 174 /** Access to instrument cluster through vehicle HAL using meta-data. */ 175 int METADATA = 1; 176 177 /** Access instrument cluster as a secondary display. */ 178 int GRAPHICS = 2; 179 } 180 181 @InstrumentClusterType private int mClusterType = InstrumentClusterType.UNDEFINED; 182 isInstrumentClusterAvailable()183 public boolean isInstrumentClusterAvailable() { 184 return mClusterType != InstrumentClusterType.NONE 185 && mClusterType != InstrumentClusterType.UNDEFINED; 186 } 187 getInstrumentClusterType()188 public int getInstrumentClusterType() { 189 if (mClusterType == InstrumentClusterType.UNDEFINED) { 190 synchronized (mHalSync) { 191 // TODO: need to pull this information from the HAL 192 mClusterType = getInstrumentClusterDisplay(mContext) != null 193 ? InstrumentClusterType.GRAPHICS : InstrumentClusterType.NONE; 194 } 195 } 196 return mClusterType; 197 } 198 199 @Nullable getNavigationRenderer()200 public NavigationRenderer getNavigationRenderer() { 201 return mRenderer != null ? mRenderer.getNavigationRenderer() : null; 202 } 203 registerListener(RendererInitializationListener listener)204 public void registerListener(RendererInitializationListener listener) { 205 mRendererInitializationListeners.add(listener); 206 } 207 unregisterListener(RendererInitializationListener listener)208 public void unregisterListener(RendererInitializationListener listener) { 209 mRendererInitializationListeners.remove(listener); 210 } 211 getInstrumentClusterDisplay(Context context)212 private static Display getInstrumentClusterDisplay(Context context) { 213 DisplayManager displayManager = context.getSystemService(DisplayManager.class); 214 Display[] displays = displayManager.getDisplays(); 215 216 Log.d(TAG, "There are currently " + displays.length + " displays connected."); 217 for (Display display : displays) { 218 Log.d(TAG, " " + display); 219 } 220 221 if (displays.length > 1) { 222 // TODO: assuming that secondary display is instrument cluster. Put this into settings? 223 return displays[1]; 224 } 225 return null; 226 } 227 runOnServiceThread(final Runnable runnable)228 private void runOnServiceThread(final Runnable runnable) { 229 if (Looper.myLooper() == mHandler.getLooper()) { 230 runnable.run(); 231 } else { 232 mHandler.post(runnable); 233 } 234 } 235 236 private static class TimeoutHandler extends Handler { 237 @Override handleMessage(Message msg)238 public void handleMessage(Message msg) { 239 if (msg.what == MSG_TIMEOUT) { 240 Log.e(TAG, "Renderer initialization timeout.", new RuntimeException()); 241 } else { 242 super.handleMessage(msg); 243 } 244 } 245 } 246 } 247