• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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