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