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