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.CarAppFocusManager; 19 import android.car.IAppFocus; 20 import android.car.IAppFocusListener; 21 import android.car.IAppFocusOwnershipCallback; 22 import android.content.Context; 23 import android.os.Binder; 24 import android.os.Handler; 25 import android.os.HandlerThread; 26 import android.os.Looper; 27 import android.os.Message; 28 import android.os.RemoteException; 29 import android.util.Log; 30 31 import java.io.PrintWriter; 32 import java.util.HashMap; 33 import java.util.HashSet; 34 import java.util.Map; 35 import java.util.Set; 36 import java.util.concurrent.CopyOnWriteArrayList; 37 38 /** 39 * App focus service ensures only one instance of application type is active at a time. 40 */ 41 public class AppFocusService extends IAppFocus.Stub implements CarServiceBase, 42 BinderInterfaceContainer.BinderEventHandler<IAppFocusOwnershipCallback> { 43 private static final boolean DBG = false; 44 private static final boolean DBG_EVENT = false; 45 46 private final SystemActivityMonitoringService mSystemActivityMonitoringService; 47 private final ClientHolder mAllChangeClients; 48 private final OwnershipClientHolder mAllOwnershipClients; 49 /** K: appType, V: client owning it */ 50 private final HashMap<Integer, OwnershipClientInfo> mFocusOwners = new HashMap<>(); 51 private final Set<Integer> mActiveAppTypes = new HashSet<>(); 52 private final CopyOnWriteArrayList<FocusOwnershipCallback> mFocusOwnershipCallbacks = 53 new CopyOnWriteArrayList<>(); 54 private final BinderInterfaceContainer.BinderEventHandler<IAppFocusListener> 55 mAllBinderEventHandler = bInterface -> { /* nothing to do.*/ }; 56 57 private DispatchHandler mDispatchHandler; 58 private HandlerThread mHandlerThread; 59 AppFocusService(Context context, SystemActivityMonitoringService systemActivityMonitoringService)60 public AppFocusService(Context context, 61 SystemActivityMonitoringService systemActivityMonitoringService) { 62 mSystemActivityMonitoringService = systemActivityMonitoringService; 63 mAllChangeClients = new ClientHolder(mAllBinderEventHandler); 64 mAllOwnershipClients = new OwnershipClientHolder(this); 65 } 66 67 @Override registerFocusListener(IAppFocusListener listener, int appType)68 public void registerFocusListener(IAppFocusListener listener, int appType) { 69 synchronized (this) { 70 ClientInfo info = (ClientInfo) mAllChangeClients.getBinderInterface(listener); 71 if (info == null) { 72 info = new ClientInfo(mAllChangeClients, listener, Binder.getCallingUid(), 73 Binder.getCallingPid(), appType); 74 mAllChangeClients.addBinderInterface(info); 75 } else { 76 info.addAppType(appType); 77 } 78 } 79 } 80 81 @Override unregisterFocusListener(IAppFocusListener listener, int appType)82 public void unregisterFocusListener(IAppFocusListener listener, int appType) { 83 synchronized (this) { 84 ClientInfo info = (ClientInfo) mAllChangeClients.getBinderInterface(listener); 85 if (info == null) { 86 return; 87 } 88 info.removeAppType(appType); 89 if (info.getAppTypes().isEmpty()) { 90 mAllChangeClients.removeBinder(listener); 91 } 92 } 93 } 94 95 @Override getActiveAppTypes()96 public int[] getActiveAppTypes() { 97 synchronized (this) { 98 return toIntArray(mActiveAppTypes); 99 } 100 } 101 102 @Override isOwningFocus(IAppFocusOwnershipCallback callback, int appType)103 public boolean isOwningFocus(IAppFocusOwnershipCallback callback, int appType) { 104 synchronized (this) { 105 OwnershipClientInfo info = 106 (OwnershipClientInfo) mAllOwnershipClients.getBinderInterface(callback); 107 if (info == null) { 108 return false; 109 } 110 return info.getOwnedAppTypes().contains(appType); 111 } 112 } 113 114 @Override requestAppFocus(IAppFocusOwnershipCallback callback, int appType)115 public int requestAppFocus(IAppFocusOwnershipCallback callback, int appType) { 116 synchronized (this) { 117 OwnershipClientInfo info = 118 (OwnershipClientInfo) mAllOwnershipClients.getBinderInterface(callback); 119 if (info == null) { 120 info = new OwnershipClientInfo(mAllOwnershipClients, callback, 121 Binder.getCallingUid(), Binder.getCallingPid()); 122 mAllOwnershipClients.addBinderInterface(info); 123 } 124 Set<Integer> alreadyOwnedAppTypes = info.getOwnedAppTypes(); 125 if (!alreadyOwnedAppTypes.contains(appType)) { 126 OwnershipClientInfo ownerInfo = mFocusOwners.get(appType); 127 if (ownerInfo != null && ownerInfo != info) { 128 if (mSystemActivityMonitoringService.isInForeground( 129 ownerInfo.getPid(), ownerInfo.getUid()) && 130 !mSystemActivityMonitoringService.isInForeground( 131 info.getPid(), info.getUid())) { 132 Log.w(CarLog.TAG_APP_FOCUS, "Focus request failed for non-foreground app(" 133 + "pid=" + info.getPid() + ", uid=" + info.getUid() + ")." 134 + "Foreground app (pid=" + ownerInfo.getPid() + ", uid=" 135 + ownerInfo.getUid() + ") owns it."); 136 return CarAppFocusManager.APP_FOCUS_REQUEST_FAILED; 137 } 138 ownerInfo.removeOwnedAppType(appType); 139 mDispatchHandler.requestAppFocusOwnershipLossDispatch( 140 ownerInfo.binderInterface, appType); 141 if (DBG) { 142 Log.i(CarLog.TAG_APP_FOCUS, "losing app type " 143 + appType + "," + ownerInfo.toString()); 144 } 145 } 146 updateFocusOwner(appType, info); 147 } 148 info.addOwnedAppType(appType); 149 mDispatchHandler.requestAppFocusOwnershipGrantDispatch( 150 info.binderInterface, appType); 151 if (mActiveAppTypes.add(appType)) { 152 if (DBG) { 153 Log.i(CarLog.TAG_APP_FOCUS, "adding active app type " + appType + "," 154 + info.toString()); 155 } 156 for (BinderInterfaceContainer.BinderInterface<IAppFocusListener> client : 157 mAllChangeClients.getInterfaces()) { 158 ClientInfo clientInfo = (ClientInfo) client; 159 // dispatch events only when there is change after filter and the listener 160 // is not coming from the current caller. 161 if (clientInfo.getAppTypes().contains(appType)) { 162 mDispatchHandler.requestAppFocusChangeDispatch(clientInfo.binderInterface, 163 appType, true); 164 } 165 } 166 } 167 } 168 return CarAppFocusManager.APP_FOCUS_REQUEST_SUCCEEDED; 169 } 170 171 @Override abandonAppFocus(IAppFocusOwnershipCallback callback, int appType)172 public void abandonAppFocus(IAppFocusOwnershipCallback callback, int appType) { 173 synchronized (this) { 174 OwnershipClientInfo info = 175 (OwnershipClientInfo) mAllOwnershipClients.getBinderInterface(callback); 176 if (info == null) { 177 // ignore as this client cannot have owned anything. 178 return; 179 } 180 if (!mActiveAppTypes.contains(appType)) { 181 // ignore as none of them are active; 182 return; 183 } 184 Set<Integer> currentlyOwnedAppTypes = info.getOwnedAppTypes(); 185 if (!currentlyOwnedAppTypes.contains(appType)) { 186 // ignore as listener doesn't own focus. 187 return; 188 } 189 if (mFocusOwners.remove(appType) != null) { 190 mActiveAppTypes.remove(appType); 191 info.removeOwnedAppType(appType); 192 if (DBG) { 193 Log.i(CarLog.TAG_APP_FOCUS, "abandoning focus " + appType 194 + "," + info.toString()); 195 } 196 for (FocusOwnershipCallback ownershipCallback : mFocusOwnershipCallbacks) { 197 ownershipCallback.onFocusAbandoned(appType, info.mUid, info.mPid); 198 } 199 for (BinderInterfaceContainer.BinderInterface<IAppFocusListener> client : 200 mAllChangeClients.getInterfaces()) { 201 ClientInfo clientInfo = (ClientInfo) client; 202 if (clientInfo.getAppTypes().contains(appType)) { 203 mDispatchHandler.requestAppFocusChangeDispatch(clientInfo.binderInterface, 204 appType, false); 205 } 206 } 207 } 208 } 209 } 210 211 @Override init()212 public void init() { 213 synchronized (this) { 214 mHandlerThread = new HandlerThread(AppFocusService.class.getSimpleName()); 215 mHandlerThread.start(); 216 mDispatchHandler = new DispatchHandler(mHandlerThread.getLooper()); 217 } 218 } 219 220 @Override release()221 public void release() { 222 synchronized (this) { 223 mHandlerThread.quitSafely(); 224 try { 225 mHandlerThread.join(1000); 226 } catch (InterruptedException e) { 227 Log.e(CarLog.TAG_APP_FOCUS, "Timeout while waiting for handler thread to join."); 228 } 229 mDispatchHandler = null; 230 mAllChangeClients.clear(); 231 mAllOwnershipClients.clear(); 232 mFocusOwners.clear(); 233 mActiveAppTypes.clear(); 234 } 235 } 236 237 @Override onBinderDeath( BinderInterfaceContainer.BinderInterface<IAppFocusOwnershipCallback> bInterface)238 public void onBinderDeath( 239 BinderInterfaceContainer.BinderInterface<IAppFocusOwnershipCallback> bInterface) { 240 OwnershipClientInfo info = (OwnershipClientInfo) bInterface; 241 for (Integer appType : info.getOwnedAppTypes()) { 242 abandonAppFocus(bInterface.binderInterface, appType); 243 } 244 } 245 246 @Override dump(PrintWriter writer)247 public void dump(PrintWriter writer) { 248 writer.println("**AppFocusService**"); 249 synchronized (this) { 250 writer.println("mActiveAppTypes:" + mActiveAppTypes); 251 for (BinderInterfaceContainer.BinderInterface<IAppFocusOwnershipCallback> client : 252 mAllOwnershipClients.getInterfaces()) { 253 OwnershipClientInfo clientInfo = (OwnershipClientInfo) client; 254 writer.println(clientInfo.toString()); 255 } 256 } 257 } 258 259 /** 260 * Returns true if process with given uid and pid owns provided focus. 261 */ isFocusOwner(int uid, int pid, int appType)262 public boolean isFocusOwner(int uid, int pid, int appType) { 263 synchronized (this) { 264 if (mFocusOwners.containsKey(appType)) { 265 OwnershipClientInfo clientInfo = mFocusOwners.get(appType); 266 return clientInfo.getUid() == uid && clientInfo.getPid() == pid; 267 } 268 } 269 return false; 270 } 271 272 /** 273 * Defines callback functions that will be called when ownership has been changed. 274 */ 275 public interface FocusOwnershipCallback { onFocusAcquired(int appType, int uid, int pid)276 void onFocusAcquired(int appType, int uid, int pid); onFocusAbandoned(int appType, int uid, int pid)277 void onFocusAbandoned(int appType, int uid, int pid); 278 } 279 280 /** 281 * Registers callback. 282 * 283 * If any focus already acquired it will trigger 284 * {@link FocusOwnershipCallback#onFocusAcquired} call immediately in the same thread. 285 */ registerContextOwnerChangedCallback(FocusOwnershipCallback callback)286 public void registerContextOwnerChangedCallback(FocusOwnershipCallback callback) { 287 mFocusOwnershipCallbacks.add(callback); 288 289 HashSet<Map.Entry<Integer, OwnershipClientInfo>> owners; 290 synchronized (this) { 291 owners = new HashSet<>(mFocusOwners.entrySet()); 292 } 293 294 for (Map.Entry<Integer, OwnershipClientInfo> entry : owners) { 295 OwnershipClientInfo clientInfo = entry.getValue(); 296 callback.onFocusAcquired(entry.getKey(), clientInfo.getUid(), clientInfo.getPid()); 297 } 298 } 299 300 /** 301 * Unregisters provided callback. 302 */ unregisterContextOwnerChangedCallback(FocusOwnershipCallback callback)303 public void unregisterContextOwnerChangedCallback(FocusOwnershipCallback callback) { 304 mFocusOwnershipCallbacks.remove(callback); 305 } 306 updateFocusOwner(int appType, OwnershipClientInfo owner)307 private void updateFocusOwner(int appType, OwnershipClientInfo owner) { 308 synchronized (this) { 309 mFocusOwners.put(appType, owner); 310 } 311 312 CarServiceUtils.runOnMain(() -> { 313 for (FocusOwnershipCallback callback : mFocusOwnershipCallbacks) { 314 callback.onFocusAcquired(appType, owner.getUid(), owner.getPid()); 315 } 316 }); 317 } 318 dispatchAppFocusOwnershipLoss(IAppFocusOwnershipCallback callback, int appType)319 private void dispatchAppFocusOwnershipLoss(IAppFocusOwnershipCallback callback, int appType) { 320 try { 321 callback.onAppFocusOwnershipLost(appType); 322 } catch (RemoteException e) { 323 } 324 } 325 dispatchAppFocusOwnershipGrant(IAppFocusOwnershipCallback callback, int appType)326 private void dispatchAppFocusOwnershipGrant(IAppFocusOwnershipCallback callback, int appType) { 327 try { 328 callback.onAppFocusOwnershipGranted(appType); 329 } catch (RemoteException e) { 330 } 331 } 332 dispatchAppFocusChange(IAppFocusListener listener, int appType, boolean active)333 private void dispatchAppFocusChange(IAppFocusListener listener, int appType, boolean active) { 334 try { 335 listener.onAppFocusChanged(appType, active); 336 } catch (RemoteException e) { 337 } 338 } 339 340 private static class ClientHolder extends BinderInterfaceContainer<IAppFocusListener> { ClientHolder(BinderEventHandler<IAppFocusListener> holder)341 private ClientHolder(BinderEventHandler<IAppFocusListener> holder) { 342 super(holder); 343 } 344 } 345 346 private static class OwnershipClientHolder extends 347 BinderInterfaceContainer<IAppFocusOwnershipCallback> { OwnershipClientHolder(AppFocusService service)348 private OwnershipClientHolder(AppFocusService service) { 349 super(service); 350 } 351 } 352 353 private static class ClientInfo extends 354 BinderInterfaceContainer.BinderInterface<IAppFocusListener> { 355 private final int mUid; 356 private final int mPid; 357 private final Set<Integer> mAppTypes = new HashSet<>(); 358 ClientInfo(ClientHolder holder, IAppFocusListener binder, int uid, int pid, int appType)359 private ClientInfo(ClientHolder holder, IAppFocusListener binder, int uid, int pid, 360 int appType) { 361 super(holder, binder); 362 this.mUid = uid; 363 this.mPid = pid; 364 this.mAppTypes.add(appType); 365 } 366 getAppTypes()367 private synchronized Set<Integer> getAppTypes() { 368 return mAppTypes; 369 } 370 addAppType(Integer appType)371 private synchronized boolean addAppType(Integer appType) { 372 return mAppTypes.add(appType); 373 } 374 removeAppType(Integer appType)375 private synchronized boolean removeAppType(Integer appType) { 376 return mAppTypes.remove(appType); 377 } 378 379 @Override toString()380 public String toString() { 381 synchronized (this) { 382 return "ClientInfo{mUid=" + mUid + ",mPid=" + mPid 383 + ",appTypes=" + mAppTypes + "}"; 384 } 385 } 386 } 387 388 private static class OwnershipClientInfo extends 389 BinderInterfaceContainer.BinderInterface<IAppFocusOwnershipCallback> { 390 private final int mUid; 391 private final int mPid; 392 private final Set<Integer> mOwnedAppTypes = new HashSet<>(); 393 OwnershipClientInfo(OwnershipClientHolder holder, IAppFocusOwnershipCallback binder, int uid, int pid)394 private OwnershipClientInfo(OwnershipClientHolder holder, IAppFocusOwnershipCallback binder, 395 int uid, int pid) { 396 super(holder, binder); 397 this.mUid = uid; 398 this.mPid = pid; 399 } 400 getOwnedAppTypes()401 private synchronized Set<Integer> getOwnedAppTypes() { 402 if (DBG_EVENT) { 403 Log.i(CarLog.TAG_APP_FOCUS, "getOwnedAppTypes " + mOwnedAppTypes); 404 } 405 return mOwnedAppTypes; 406 } 407 addOwnedAppType(Integer appType)408 private synchronized boolean addOwnedAppType(Integer appType) { 409 if (DBG_EVENT) { 410 Log.i(CarLog.TAG_APP_FOCUS, "addOwnedAppType " + appType); 411 } 412 return mOwnedAppTypes.add(appType); 413 } 414 removeOwnedAppType(Integer appType)415 private synchronized boolean removeOwnedAppType(Integer appType) { 416 if (DBG_EVENT) { 417 Log.i(CarLog.TAG_APP_FOCUS, "removeOwnedAppType " + appType); 418 } 419 return mOwnedAppTypes.remove(appType); 420 } 421 getUid()422 int getUid() { 423 return mUid; 424 } 425 getPid()426 int getPid() { 427 return mPid; 428 } 429 430 @Override toString()431 public String toString() { 432 synchronized (this) { 433 return "ClientInfo{mUid=" + mUid + ",mPid=" + mPid 434 + ",owned=" + mOwnedAppTypes + "}"; 435 } 436 } 437 } 438 439 private class DispatchHandler extends Handler { 440 private static final int MSG_DISPATCH_OWNERSHIP_LOSS = 0; 441 private static final int MSG_DISPATCH_OWNERSHIP_GRANT = 1; 442 private static final int MSG_DISPATCH_FOCUS_CHANGE = 2; 443 DispatchHandler(Looper looper)444 private DispatchHandler(Looper looper) { 445 super(looper); 446 } 447 requestAppFocusOwnershipLossDispatch(IAppFocusOwnershipCallback callback, int appType)448 private void requestAppFocusOwnershipLossDispatch(IAppFocusOwnershipCallback callback, 449 int appType) { 450 Message msg = obtainMessage(MSG_DISPATCH_OWNERSHIP_LOSS, appType, 0, callback); 451 sendMessage(msg); 452 } 453 requestAppFocusOwnershipGrantDispatch(IAppFocusOwnershipCallback callback, int appType)454 private void requestAppFocusOwnershipGrantDispatch(IAppFocusOwnershipCallback callback, 455 int appType) { 456 Message msg = obtainMessage(MSG_DISPATCH_OWNERSHIP_GRANT, appType, 0, callback); 457 sendMessage(msg); 458 } 459 requestAppFocusChangeDispatch(IAppFocusListener listener, int appType, boolean active)460 private void requestAppFocusChangeDispatch(IAppFocusListener listener, int appType, 461 boolean active) { 462 Message msg = obtainMessage(MSG_DISPATCH_FOCUS_CHANGE, appType, active ? 1 : 0, 463 listener); 464 sendMessage(msg); 465 } 466 467 @Override handleMessage(Message msg)468 public void handleMessage(Message msg) { 469 switch (msg.what) { 470 case MSG_DISPATCH_OWNERSHIP_LOSS: 471 dispatchAppFocusOwnershipLoss((IAppFocusOwnershipCallback) msg.obj, msg.arg1); 472 break; 473 case MSG_DISPATCH_OWNERSHIP_GRANT: 474 dispatchAppFocusOwnershipGrant((IAppFocusOwnershipCallback) msg.obj, msg.arg1); 475 break; 476 case MSG_DISPATCH_FOCUS_CHANGE: 477 dispatchAppFocusChange((IAppFocusListener) msg.obj, msg.arg1, msg.arg2 == 1); 478 break; 479 default: 480 Log.e(CarLog.TAG_APP_FOCUS, "Can't dispatch message: " + msg); 481 } 482 } 483 } 484 toIntArray(Set<Integer> intSet)485 private static int[] toIntArray(Set<Integer> intSet) { 486 int[] intArr = new int[intSet.size()]; 487 int index = 0; 488 for (Integer value : intSet) { 489 intArr[index++] = value; 490 } 491 return intArr; 492 } 493 } 494