• 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.renderer.IInstrumentCluster;
22 import android.car.cluster.renderer.IInstrumentClusterNavigation;
23 import android.content.ComponentName;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.ServiceConnection;
27 import android.os.IBinder;
28 import android.os.RemoteException;
29 import android.text.TextUtils;
30 import android.util.Log;
31 import android.view.KeyEvent;
32 
33 import com.android.car.AppFocusService;
34 import com.android.car.AppFocusService.FocusOwnershipCallback;
35 import com.android.car.CarInputService;
36 import com.android.car.CarInputService.KeyEventListener;
37 import com.android.car.CarLog;
38 import com.android.car.CarServiceBase;
39 import com.android.car.R;
40 import com.android.internal.annotations.GuardedBy;
41 
42 import java.io.PrintWriter;
43 
44 /**
45  * Service responsible for interaction with car's instrument cluster.
46  *
47  * @hide
48  */
49 @SystemApi
50 public class InstrumentClusterService implements CarServiceBase,
51         FocusOwnershipCallback, KeyEventListener {
52 
53     private static final String TAG = CarLog.TAG_CLUSTER;
54     private static final Boolean DBG = false;
55 
56     private final Context mContext;
57     private final AppFocusService mAppFocusService;
58     private final CarInputService mCarInputService;
59     private final Object mSync = new Object();
60 
61     @GuardedBy("mSync")
62     private ContextOwner mNavContextOwner;
63     @GuardedBy("mSync")
64     private IInstrumentCluster mRendererService;
65 
66     private boolean mRendererBound = false;
67 
68     private final ServiceConnection mRendererServiceConnection = new ServiceConnection() {
69         @Override
70         public void onServiceConnected(ComponentName name, IBinder binder) {
71             if (DBG) {
72                 Log.d(TAG, "onServiceConnected, name: " + name + ", binder: " + binder);
73             }
74             IInstrumentCluster service = IInstrumentCluster.Stub.asInterface(binder);
75             ContextOwner navContextOwner;
76             synchronized (mSync) {
77                 mRendererService = service;
78                 navContextOwner = mNavContextOwner;
79             }
80             if (navContextOwner !=  null && service != null) {
81                 notifyNavContextOwnerChanged(service, navContextOwner.uid, navContextOwner.pid);
82             }
83         }
84 
85         @Override
86         public void onServiceDisconnected(ComponentName name) {
87             Log.d(TAG, "onServiceDisconnected, name: " + name);
88             mRendererService = null;
89             // Try to rebind with instrument cluster.
90             mRendererBound = bindInstrumentClusterRendererService();
91         }
92     };
93 
InstrumentClusterService(Context context, AppFocusService appFocusService, CarInputService carInputService)94     public InstrumentClusterService(Context context, AppFocusService appFocusService,
95             CarInputService carInputService) {
96         mContext = context;
97         mAppFocusService = appFocusService;
98         mCarInputService = carInputService;
99     }
100 
101     @Override
init()102     public void init() {
103         if (DBG) {
104             Log.d(TAG, "init");
105         }
106 
107         mAppFocusService.registerContextOwnerChangedCallback(this /* FocusOwnershipCallback */);
108         mCarInputService.setInstrumentClusterKeyListener(this /* KeyEventListener */);
109         mRendererBound = bindInstrumentClusterRendererService();
110     }
111 
112     @Override
release()113     public void release() {
114         if (DBG) {
115             Log.d(TAG, "release");
116         }
117 
118         mAppFocusService.unregisterContextOwnerChangedCallback(this);
119         if (mRendererBound) {
120             mContext.unbindService(mRendererServiceConnection);
121             mRendererBound = false;
122         }
123     }
124 
125     @Override
dump(PrintWriter writer)126     public void dump(PrintWriter writer) {
127         writer.println("**" + getClass().getSimpleName() + "**");
128         writer.println("bound with renderer: " + mRendererBound);
129         writer.println("renderer service: " + mRendererService);
130     }
131 
132     @Override
onFocusAcquired(int appType, int uid, int pid)133     public void onFocusAcquired(int appType, int uid, int pid) {
134         if (appType != CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION) {
135             return;
136         }
137 
138         IInstrumentCluster service;
139         synchronized (mSync) {
140             mNavContextOwner = new ContextOwner(uid, pid);
141             service = mRendererService;
142         }
143 
144         if (service != null) {
145             notifyNavContextOwnerChanged(service, uid, pid);
146         }
147     }
148 
149     @Override
onFocusAbandoned(int appType, int uid, int pid)150     public void onFocusAbandoned(int appType, int uid, int pid) {
151         if (appType != CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION) {
152             return;
153         }
154 
155         IInstrumentCluster service;
156         synchronized (mSync) {
157             if (mNavContextOwner == null
158                     || mNavContextOwner.uid != uid
159                     || mNavContextOwner.pid != pid) {
160                 return;  // Nothing to do here, no active focus or not owned by this client.
161             }
162 
163             mNavContextOwner = null;
164             service = mRendererService;
165         }
166 
167         if (service != null) {
168             notifyNavContextOwnerChanged(service, 0, 0);
169         }
170     }
171 
notifyNavContextOwnerChanged(IInstrumentCluster service, int uid, int pid)172     private static void notifyNavContextOwnerChanged(IInstrumentCluster service, int uid, int pid) {
173         try {
174             service.setNavigationContextOwner(uid, pid);
175         } catch (RemoteException e) {
176             Log.e(TAG, "Failed to call setNavigationContextOwner", e);
177         }
178     }
179 
bindInstrumentClusterRendererService()180     private boolean bindInstrumentClusterRendererService() {
181         String rendererService = mContext.getString(R.string.instrumentClusterRendererService);
182         if (TextUtils.isEmpty(rendererService)) {
183             Log.i(TAG, "Instrument cluster renderer was not configured");
184             return false;
185         }
186 
187         Log.d(TAG, "bindInstrumentClusterRendererService, component: " + rendererService);
188 
189         Intent intent = new Intent();
190         intent.setComponent(ComponentName.unflattenFromString(rendererService));
191         return mContext.bindService(intent, mRendererServiceConnection, Context.BIND_AUTO_CREATE);
192     }
193 
194     @Nullable
getNavigationService()195     public IInstrumentClusterNavigation getNavigationService() {
196         IInstrumentCluster service;
197         synchronized (mSync) {
198             service = mRendererService;
199         }
200 
201         try {
202             return service == null ? null : service.getNavigationService();
203         } catch (RemoteException e) {
204             Log.e(TAG, "getNavigationServiceBinder" , e);
205             return null;
206         }
207     }
208 
209     @Override
onKeyEvent(KeyEvent event)210     public boolean onKeyEvent(KeyEvent event) {
211         if (DBG) {
212             Log.d(TAG, "InstrumentClusterService#onKeyEvent: " + event);
213         }
214 
215         IInstrumentCluster service;
216         synchronized (mSync) {
217             service = mRendererService;
218         }
219 
220         if (service != null) {
221             try {
222                 service.onKeyEvent(event);
223             } catch (RemoteException e) {
224                 Log.e(TAG, "onKeyEvent", e);
225             }
226         }
227         return true;
228     }
229 
230     private static class ContextOwner {
231         final int uid;
232         final int pid;
233 
ContextOwner(int uid, int pid)234         ContextOwner(int uid, int pid) {
235             this.uid = uid;
236             this.pid = pid;
237         }
238     }
239 }
240