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