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 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 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 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 82 public int getActiveAppContexts() { 83 synchronized (this) { 84 return mActiveContexts; 85 } 86 } 87 88 @Override 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 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 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 193 public void init() { 194 // nothing to do 195 } 196 197 @Override 198 public void release() { 199 synchronized (this) { 200 mAllClients.clear(); 201 mContextOwners.clear(); 202 mActiveContexts = 0; 203 } 204 } 205 206 @Override 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 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 */ 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 242 private void dispatchAppContextOwnershipLoss(IAppContextListener listener, int contexts) { 243 try { 244 listener.onAppContextOwnershipLoss(contexts); 245 } catch (RemoteException e) { 246 } 247 } 248 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> { 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 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 278 private synchronized int getFilter() { 279 return mFilter; 280 } 281 282 private synchronized void setFilter(int filter) { 283 mFilter = filter; 284 } 285 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 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 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 315 private DispatchHandler(Looper looper) { 316 super(looper); 317 } 318 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 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 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