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