• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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