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