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 android.annotation.Nullable; 19 import android.annotation.SystemApi; 20 import android.car.CarAppFocusManager; 21 import android.car.cluster.IInstrumentClusterManagerCallback; 22 import android.car.cluster.IInstrumentClusterManagerService; 23 import android.car.cluster.renderer.IInstrumentCluster; 24 import android.car.cluster.renderer.IInstrumentClusterNavigation; 25 import android.content.ComponentName; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.ServiceConnection; 29 import android.os.Handler; 30 import android.os.IBinder; 31 import android.os.Message; 32 import android.os.RemoteException; 33 import android.os.UserHandle; 34 import android.text.TextUtils; 35 import android.util.Log; 36 import android.view.KeyEvent; 37 38 import com.android.car.AppFocusService; 39 import com.android.car.AppFocusService.FocusOwnershipCallback; 40 import com.android.car.CarInputService; 41 import com.android.car.CarInputService.KeyEventListener; 42 import com.android.car.CarLocalServices; 43 import com.android.car.CarLog; 44 import com.android.car.CarServiceBase; 45 import com.android.car.R; 46 import com.android.car.user.CarUserService; 47 import com.android.internal.annotations.GuardedBy; 48 49 import java.io.PrintWriter; 50 import java.util.Objects; 51 52 /** 53 * Service responsible for interaction with car's instrument cluster. 54 * 55 * @hide 56 */ 57 @SystemApi 58 public class InstrumentClusterService implements CarServiceBase, FocusOwnershipCallback, 59 KeyEventListener { 60 private static final String TAG = CarLog.TAG_CLUSTER; 61 private static final ContextOwner NO_OWNER = new ContextOwner(0, 0); 62 63 private final Context mContext; 64 private final AppFocusService mAppFocusService; 65 private final CarInputService mCarInputService; 66 /** 67 * TODO: (b/121277787) Remove this on master. 68 * @deprecated CarInstrumentClusterManager is being deprecated. 69 */ 70 @Deprecated 71 private final ClusterManagerService mClusterManagerService = new ClusterManagerService(); 72 private final Object mSync = new Object(); 73 @GuardedBy("mSync") 74 private ContextOwner mNavContextOwner = NO_OWNER; 75 @GuardedBy("mSync") 76 private IInstrumentCluster mRendererService; 77 // If renderer service crashed / stopped and this class fails to rebind with it immediately, 78 // we should wait some time before next attempt. This may happen during APK update for example. 79 private DeferredRebinder mDeferredRebinder; 80 // Whether {@link android.car.cluster.renderer.InstrumentClusterRendererService} is bound 81 // (although not necessarily connected) 82 private boolean mRendererBound = false; 83 84 /** 85 * Connection to {@link android.car.cluster.renderer.InstrumentClusterRendererService} 86 */ 87 private final ServiceConnection mRendererServiceConnection = new ServiceConnection() { 88 @Override 89 public void onServiceConnected(ComponentName name, IBinder binder) { 90 if (Log.isLoggable(TAG, Log.DEBUG)) { 91 Log.d(TAG, "onServiceConnected, name: " + name + ", binder: " + binder); 92 } 93 IInstrumentCluster service = IInstrumentCluster.Stub.asInterface(binder); 94 ContextOwner navContextOwner; 95 synchronized (mSync) { 96 mRendererService = service; 97 navContextOwner = mNavContextOwner; 98 } 99 if (navContextOwner != null && service != null) { 100 notifyNavContextOwnerChanged(service, navContextOwner); 101 } 102 } 103 104 @Override 105 public void onServiceDisconnected(ComponentName name) { 106 if (Log.isLoggable(TAG, Log.DEBUG)) { 107 Log.d(TAG, "onServiceDisconnected, name: " + name); 108 } 109 mContext.unbindService(this); 110 mRendererBound = false; 111 112 synchronized (mSync) { 113 mRendererService = null; 114 } 115 116 if (mDeferredRebinder == null) { 117 mDeferredRebinder = new DeferredRebinder(); 118 } 119 mDeferredRebinder.rebind(); 120 } 121 }; 122 InstrumentClusterService(Context context, AppFocusService appFocusService, CarInputService carInputService)123 public InstrumentClusterService(Context context, AppFocusService appFocusService, 124 CarInputService carInputService) { 125 mContext = context; 126 mAppFocusService = appFocusService; 127 mCarInputService = carInputService; 128 } 129 130 @Override init()131 public void init() { 132 if (Log.isLoggable(TAG, Log.DEBUG)) { 133 Log.d(TAG, "init"); 134 } 135 136 mAppFocusService.registerContextOwnerChangedCallback(this /* FocusOwnershipCallback */); 137 mCarInputService.setInstrumentClusterKeyListener(this /* KeyEventListener */); 138 // TODO(b/124246323) Start earlier once data storage for cluster is clarified 139 // for early boot. 140 CarLocalServices.getService(CarUserService.class).runOnUser0Unlock(() -> { 141 mRendererBound = bindInstrumentClusterRendererService(); 142 }); 143 } 144 145 @Override release()146 public void release() { 147 if (Log.isLoggable(TAG, Log.DEBUG)) { 148 Log.d(TAG, "release"); 149 } 150 151 mAppFocusService.unregisterContextOwnerChangedCallback(this); 152 if (mRendererBound) { 153 mContext.unbindService(mRendererServiceConnection); 154 mRendererBound = false; 155 } 156 } 157 158 @Override dump(PrintWriter writer)159 public void dump(PrintWriter writer) { 160 writer.println("**" + getClass().getSimpleName() + "**"); 161 writer.println("bound with renderer: " + mRendererBound); 162 writer.println("renderer service: " + mRendererService); 163 writer.println("context owner: " + mNavContextOwner); 164 } 165 166 @Override onFocusAcquired(int appType, int uid, int pid)167 public void onFocusAcquired(int appType, int uid, int pid) { 168 changeNavContextOwner(appType, uid, pid, true); 169 } 170 171 @Override onFocusAbandoned(int appType, int uid, int pid)172 public void onFocusAbandoned(int appType, int uid, int pid) { 173 changeNavContextOwner(appType, uid, pid, false); 174 } 175 changeNavContextOwner(int appType, int uid, int pid, boolean acquire)176 private void changeNavContextOwner(int appType, int uid, int pid, boolean acquire) { 177 if (appType != CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION) { 178 return; 179 } 180 181 IInstrumentCluster service; 182 ContextOwner requester = new ContextOwner(uid, pid); 183 ContextOwner newOwner = acquire ? requester : NO_OWNER; 184 synchronized (mSync) { 185 if ((acquire && Objects.equals(mNavContextOwner, requester)) 186 || (!acquire && !Objects.equals(mNavContextOwner, requester))) { 187 // Nothing to do here. Either the same owner is acquiring twice, or someone is 188 // abandoning a focus they didn't have. 189 Log.w(TAG, "Invalid nav context owner change (acquiring: " + acquire 190 + "), current owner: [" + mNavContextOwner 191 + "], requester: [" + requester + "]"); 192 return; 193 } 194 195 mNavContextOwner = newOwner; 196 service = mRendererService; 197 } 198 199 if (service != null) { 200 notifyNavContextOwnerChanged(service, newOwner); 201 } 202 } 203 notifyNavContextOwnerChanged(IInstrumentCluster service, ContextOwner owner)204 private static void notifyNavContextOwnerChanged(IInstrumentCluster service, 205 ContextOwner owner) { 206 try { 207 service.setNavigationContextOwner(owner.uid, owner.pid); 208 } catch (RemoteException e) { 209 Log.e(TAG, "Failed to call setNavigationContextOwner", e); 210 } 211 } 212 bindInstrumentClusterRendererService()213 private boolean bindInstrumentClusterRendererService() { 214 String rendererService = mContext.getString(R.string.instrumentClusterRendererService); 215 if (TextUtils.isEmpty(rendererService)) { 216 Log.i(TAG, "Instrument cluster renderer was not configured"); 217 return false; 218 } 219 220 Log.d(TAG, "bindInstrumentClusterRendererService, component: " + rendererService); 221 222 Intent intent = new Intent(); 223 intent.setComponent(ComponentName.unflattenFromString(rendererService)); 224 return mContext.bindServiceAsUser(intent, mRendererServiceConnection, 225 Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM); 226 } 227 228 @Nullable getNavigationService()229 public IInstrumentClusterNavigation getNavigationService() { 230 try { 231 IInstrumentCluster service = getInstrumentClusterRendererService(); 232 return service == null ? null : service.getNavigationService(); 233 } catch (RemoteException e) { 234 Log.e(TAG, "getNavigationServiceBinder" , e); 235 return null; 236 } 237 } 238 239 /** 240 * @deprecated {@link android.car.cluster.CarInstrumentClusterManager} is now deprecated. 241 */ 242 @Deprecated getManagerService()243 public IInstrumentClusterManagerService.Stub getManagerService() { 244 return mClusterManagerService; 245 } 246 247 @Override onKeyEvent(KeyEvent event)248 public void onKeyEvent(KeyEvent event) { 249 if (Log.isLoggable(TAG, Log.DEBUG)) { 250 Log.d(TAG, "InstrumentClusterService#onKeyEvent: " + event); 251 } 252 253 IInstrumentCluster service = getInstrumentClusterRendererService(); 254 if (service != null) { 255 try { 256 service.onKeyEvent(event); 257 } catch (RemoteException e) { 258 Log.e(TAG, "onKeyEvent", e); 259 } 260 } 261 } 262 getInstrumentClusterRendererService()263 private IInstrumentCluster getInstrumentClusterRendererService() { 264 IInstrumentCluster service; 265 synchronized (mSync) { 266 service = mRendererService; 267 } 268 return service; 269 } 270 271 private static class ContextOwner { 272 final int uid; 273 final int pid; 274 ContextOwner(int uid, int pid)275 ContextOwner(int uid, int pid) { 276 this.uid = uid; 277 this.pid = pid; 278 } 279 280 @Override toString()281 public String toString() { 282 return "uid: " + uid + ", pid: " + pid; 283 } 284 285 @Override equals(Object o)286 public boolean equals(Object o) { 287 if (this == o) return true; 288 if (o == null || getClass() != o.getClass()) return false; 289 ContextOwner that = (ContextOwner) o; 290 return uid == that.uid && pid == that.pid; 291 } 292 293 @Override hashCode()294 public int hashCode() { 295 return Objects.hash(uid, pid); 296 } 297 } 298 299 /** 300 * TODO: (b/121277787) Remove on master 301 * @deprecated CarClusterManager is being deprecated. 302 */ 303 @Deprecated 304 private class ClusterManagerService extends IInstrumentClusterManagerService.Stub { 305 @Override startClusterActivity(Intent intent)306 public void startClusterActivity(Intent intent) throws RemoteException { 307 // No op. 308 } 309 310 @Override registerCallback(IInstrumentClusterManagerCallback callback)311 public void registerCallback(IInstrumentClusterManagerCallback callback) 312 throws RemoteException { 313 // No op. 314 } 315 316 @Override unregisterCallback(IInstrumentClusterManagerCallback callback)317 public void unregisterCallback(IInstrumentClusterManagerCallback callback) 318 throws RemoteException { 319 // No op. 320 } 321 } 322 323 private class DeferredRebinder extends Handler { 324 private static final long NEXT_REBIND_ATTEMPT_DELAY_MS = 1000L; 325 private static final int NUMBER_OF_ATTEMPTS = 10; 326 rebind()327 public void rebind() { 328 mRendererBound = bindInstrumentClusterRendererService(); 329 330 if (!mRendererBound) { 331 removeMessages(0); 332 sendMessageDelayed(obtainMessage(0, NUMBER_OF_ATTEMPTS, 0), 333 NEXT_REBIND_ATTEMPT_DELAY_MS); 334 } 335 } 336 337 @Override handleMessage(Message msg)338 public void handleMessage(Message msg) { 339 mRendererBound = bindInstrumentClusterRendererService(); 340 341 if (mRendererBound) { 342 Log.w(TAG, "Failed to bound to render service, next attempt in " 343 + NEXT_REBIND_ATTEMPT_DELAY_MS + "ms."); 344 345 int attempts = msg.arg1; 346 if (--attempts >= 0) { 347 sendMessageDelayed(obtainMessage(0, attempts, 0), NEXT_REBIND_ATTEMPT_DELAY_MS); 348 } else { 349 Log.wtf(TAG, "Failed to rebind with cluster rendering service"); 350 } 351 } 352 } 353 } 354 } 355