1 /*
2  * Copyright (C) 2015 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;
17 
18 import android.car.CarAppContextManager;
19 import android.car.IAppContext;
20 import android.car.IAppContextListener;
21 import android.content.Context;
22 import android.os.Binder;
23 import android.os.Handler;
24 import android.os.HandlerThread;
25 import android.os.Looper;
26 import android.os.Message;
27 import android.os.RemoteException;
28 import android.util.Log;
29 
30 import com.android.car.hal.VehicleHal;
31 
32 import java.io.PrintWriter;
33 import java.util.HashMap;
34 
35 public class AppContextService extends IAppContext.Stub implements CarServiceBase,
36         BinderInterfaceContainer.BinderEventHandler<IAppContextListener> {
37     private static final boolean DBG = true;
38     private static final boolean DBG_EVENT = false;
39 
40     private final ClientHolder mAllClients;
41     /** K: context flag, V: client owning it */
42     private final HashMap<Integer, ClientInfo> mContextOwners = new HashMap<>();
43     private int mActiveContexts;
44 
45     private final HandlerThread mHandlerThread;
46     private final DispatchHandler mDispatchHandler;
47 
AppContextService(Context context)48     public AppContextService(Context context) {
49         mAllClients = new ClientHolder(this);
50         mHandlerThread = new HandlerThread(AppContextService.class.getSimpleName());
51         mHandlerThread.start();
52         mDispatchHandler = new DispatchHandler(mHandlerThread.getLooper());
53     }
54 
55     @Override
registerContextListener(IAppContextListener listener, int filter)56     public void registerContextListener(IAppContextListener listener, int filter) {
57         synchronized (this) {
58             ClientInfo info = (ClientInfo) mAllClients.getBinderInterface(listener);
59             if (info == null) {
60                 info = new ClientInfo(mAllClients, listener, Binder.getCallingUid(),
61                         Binder.getCallingPid(), filter);
62                 mAllClients.addBinderInterface(info);
63             } else {
64                 info.setFilter(filter);
65             }
66         }
67     }
68 
69     @Override
unregisterContextListener(IAppContextListener listener)70     public void unregisterContextListener(IAppContextListener listener) {
71         synchronized (this) {
72             ClientInfo info = (ClientInfo) mAllClients.getBinderInterface(listener);
73             if (info == null) {
74                 return;
75             }
76             resetActiveContexts(listener, info.getOwnedContexts());
77             mAllClients.removeBinder(listener);
78         }
79     }
80 
81     @Override
getActiveAppContexts()82     public int getActiveAppContexts() {
83         synchronized (this) {
84             return mActiveContexts;
85         }
86     }
87 
88     @Override
isOwningContext(IAppContextListener listener, int context)89     public boolean isOwningContext(IAppContextListener listener, int context) {
90         synchronized (this) {
91             ClientInfo info = (ClientInfo) mAllClients.getBinderInterface(listener);
92             if (info == null) {
93                 return false;
94             }
95             int ownedContexts = info.getOwnedContexts();
96             return (ownedContexts & context) == context;
97         }
98     }
99 
100     @Override
setActiveContexts(IAppContextListener listener, int contexts)101     public void setActiveContexts(IAppContextListener listener, int contexts) {
102         synchronized (this) {
103             ClientInfo info = (ClientInfo) mAllClients.getBinderInterface(listener);
104             if (info == null) {
105                 throw new IllegalStateException("listener not registered");
106             }
107             int alreadyOwnedContexts = info.getOwnedContexts();
108             int addedContexts = 0;
109             for (int c = CarAppContextManager.APP_CONTEXT_START_FLAG;
110                     c <= CarAppContextManager.APP_CONTEXT_END_FLAG; c = (c << 1)) {
111                 if ((c & contexts) != 0 && (c & alreadyOwnedContexts) == 0) {
112                     ClientInfo ownerInfo = mContextOwners.get(c);
113                     if (ownerInfo != null && ownerInfo != info) {
114                         //TODO check if current owner is having fore-ground activity. If yes,
115                         //reject request. Always grant if requestor is fore-ground activity.
116                         ownerInfo.setOwnedContexts(ownerInfo.getOwnedContexts() & ~c);
117                         mDispatchHandler.requestAppContextOwnershipLossDispatch(
118                                 ownerInfo.binderInterface, c);
119                         if (DBG) {
120                             Log.i(CarLog.TAG_APP_CONTEXT, "losing context " +
121                                     Integer.toHexString(c) + "," + ownerInfo.toString());
122                         }
123                     } else {
124                         addedContexts |= c;
125                     }
126                     mContextOwners.put(c, info);
127                 }
128             }
129             info.setOwnedContexts(alreadyOwnedContexts | contexts);
130             mActiveContexts |= addedContexts;
131             if (addedContexts != 0) {
132                 if (DBG) {
133                     Log.i(CarLog.TAG_APP_CONTEXT, "setting context " +
134                             Integer.toHexString(addedContexts) + "," + info.toString());
135                 }
136                 for (BinderInterfaceContainer.BinderInterface<IAppContextListener> client :
137                     mAllClients.getInterfaces()) {
138                     ClientInfo clientInfo = (ClientInfo) client;
139                     // dispatch events only when there is change after filter and the listener
140                     // is not coming from the current caller.
141                     int clientFilter = clientInfo.getFilter();
142                     if ((addedContexts & clientFilter) != 0 && clientInfo != info) {
143                         mDispatchHandler.requestAppContextChangeDispatch(clientInfo.binderInterface,
144                                 mActiveContexts & clientFilter);
145                     }
146                 }
147             }
148         }
149     }
150 
151     @Override
resetActiveContexts(IAppContextListener listener, int contexts)152     public void resetActiveContexts(IAppContextListener listener, int contexts) {
153         synchronized (this) {
154             ClientInfo info = (ClientInfo) mAllClients.getBinderInterface(listener);
155             if (info == null) {
156                 // ignore as this client cannot have owned anything.
157                 return;
158             }
159             if ((contexts & mActiveContexts) == 0) {
160                 // ignore as none of them are active;
161                 return;
162             }
163             int removedContexts = 0;
164             int currentlyOwnedContexts = info.getOwnedContexts();
165             for (int c = CarAppContextManager.APP_CONTEXT_START_FLAG;
166                     c <= CarAppContextManager.APP_CONTEXT_END_FLAG; c = (c << 1)) {
167                 if ((c & contexts) != 0 && (c & currentlyOwnedContexts) != 0) {
168                     removedContexts |= c;
169                     mContextOwners.remove(c);
170                 }
171             }
172             if (removedContexts != 0) {
173                 mActiveContexts &= ~removedContexts;
174                 info.setOwnedContexts(currentlyOwnedContexts & ~removedContexts);
175                 if (DBG) {
176                     Log.i(CarLog.TAG_APP_CONTEXT, "resetting context " +
177                             Integer.toHexString(removedContexts) + "," + info.toString());
178                 }
179                 for (BinderInterfaceContainer.BinderInterface<IAppContextListener> client :
180                     mAllClients.getInterfaces()) {
181                     ClientInfo clientInfo = (ClientInfo) client;
182                     int clientFilter = clientInfo.getFilter();
183                     if ((removedContexts & clientFilter) != 0 && clientInfo != info) {
184                         mDispatchHandler.requestAppContextChangeDispatch(clientInfo.binderInterface,
185                                 mActiveContexts & clientFilter);
186                     }
187                 }
188             }
189         }
190     }
191 
192     @Override
init()193     public void init() {
194         // nothing to do
195     }
196 
197     @Override
release()198     public void release() {
199         synchronized (this) {
200             mAllClients.clear();
201             mContextOwners.clear();
202             mActiveContexts = 0;
203         }
204     }
205 
206     @Override
onBinderDeath( BinderInterfaceContainer.BinderInterface<IAppContextListener> bInterface)207     public void onBinderDeath(
208             BinderInterfaceContainer.BinderInterface<IAppContextListener> bInterface) {
209         ClientInfo info = (ClientInfo) bInterface;
210         int ownedContexts = info.getOwnedContexts();
211         if (ownedContexts != 0) {
212             resetActiveContexts(bInterface.binderInterface, ownedContexts);
213         }
214     }
215 
216     @Override
dump(PrintWriter writer)217     public void dump(PrintWriter writer) {
218         writer.println("**AppContextService**");
219         synchronized (this) {
220             writer.println("mActiveContexts:" + Integer.toHexString(mActiveContexts));
221             for (BinderInterfaceContainer.BinderInterface<IAppContextListener> client :
222                 mAllClients.getInterfaces()) {
223                 ClientInfo clientInfo = (ClientInfo) client;
224                 writer.println(clientInfo.toString());
225             }
226         }
227     }
228 
229     /**
230      * Returns true if process with given uid and pid owns provided context.
231      */
isContextOwner(int uid, int pid, int context)232     public boolean isContextOwner(int uid, int pid, int context) {
233         synchronized (this) {
234             if (mContextOwners.containsKey(context)) {
235                 ClientInfo clientInfo = mContextOwners.get(context);
236                 return clientInfo.uid == uid && clientInfo.pid == pid;
237             }
238         }
239         return false;
240     }
241 
dispatchAppContextOwnershipLoss(IAppContextListener listener, int contexts)242     private void dispatchAppContextOwnershipLoss(IAppContextListener listener, int contexts) {
243         try {
244             listener.onAppContextOwnershipLoss(contexts);
245         } catch (RemoteException e) {
246         }
247     }
248 
dispatchAppContextChange(IAppContextListener listener, int contexts)249     private void dispatchAppContextChange(IAppContextListener listener, int contexts) {
250         try {
251             listener.onAppContextChange(contexts);
252         } catch (RemoteException e) {
253         }
254     }
255 
256     private static class ClientHolder extends BinderInterfaceContainer<IAppContextListener> {
ClientHolder(AppContextService service)257         private ClientHolder(AppContextService service) {
258             super(service);
259         }
260     }
261 
262     private static class ClientInfo extends
263             BinderInterfaceContainer.BinderInterface<IAppContextListener> {
264         private final int uid;
265         private final int pid;
266         private int mFilter;
267         /** contexts owned by this client */
268         private int mOwnedContexts;
269 
ClientInfo(ClientHolder holder, IAppContextListener binder, int uid, int pid, int filter)270         private ClientInfo(ClientHolder holder, IAppContextListener binder, int uid, int pid,
271                 int filter) {
272             super(holder, binder);
273             this.uid = uid;
274             this.pid = pid;
275             this.mFilter = filter;
276         }
277 
getFilter()278         private synchronized int getFilter() {
279             return mFilter;
280         }
281 
setFilter(int filter)282         private synchronized void setFilter(int filter) {
283             mFilter = filter;
284         }
285 
getOwnedContexts()286         private synchronized int getOwnedContexts() {
287             if (DBG_EVENT) {
288                 Log.i(CarLog.TAG_APP_CONTEXT, "getOwnedContexts " +
289                         Integer.toHexString(mOwnedContexts));
290             }
291             return mOwnedContexts;
292         }
293 
setOwnedContexts(int contexts)294         private synchronized void setOwnedContexts(int contexts) {
295             if (DBG_EVENT) {
296                 Log.i(CarLog.TAG_APP_CONTEXT, "setOwnedContexts " + Integer.toHexString(contexts));
297             }
298             mOwnedContexts = contexts;
299         }
300 
301         @Override
toString()302         public String toString() {
303             synchronized (this) {
304                 return "ClientInfo{uid=" + uid + ",pid=" + pid +
305                         ",filter=" + Integer.toHexString(mFilter) +
306                         ",owned=" + Integer.toHexString(mOwnedContexts) + "}";
307             }
308         }
309     }
310 
311     private class DispatchHandler extends Handler {
312         private static final int MSG_DISPATCH_OWNERSHIP_LOSS = 0;
313         private static final int MSG_DISPATCH_CONTEXT_CHANGE = 1;
314 
DispatchHandler(Looper looper)315         private DispatchHandler(Looper looper) {
316             super(looper);
317         }
318 
requestAppContextOwnershipLossDispatch(IAppContextListener listener, int contexts)319         private void requestAppContextOwnershipLossDispatch(IAppContextListener listener,
320                 int contexts) {
321             Message msg = obtainMessage(MSG_DISPATCH_OWNERSHIP_LOSS, contexts, 0, listener);
322             sendMessage(msg);
323         }
324 
requestAppContextChangeDispatch(IAppContextListener listener, int contexts)325         private void requestAppContextChangeDispatch(IAppContextListener listener, int contexts) {
326             Message msg = obtainMessage(MSG_DISPATCH_CONTEXT_CHANGE, contexts, 0, listener);
327             sendMessage(msg);
328         }
329 
330         @Override
handleMessage(Message msg)331         public void handleMessage(Message msg) {
332             switch (msg.what) {
333                 case MSG_DISPATCH_OWNERSHIP_LOSS:
334                     dispatchAppContextOwnershipLoss((IAppContextListener) msg.obj, msg.arg1);
335                     break;
336                 case MSG_DISPATCH_CONTEXT_CHANGE:
337                     dispatchAppContextChange((IAppContextListener) msg.obj, msg.arg1);
338                     break;
339             }
340         }
341     }
342 }
343