1 /*
2  * Copyright (C) 2020 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 
17 package com.android.server.location.contexthub;
18 
19 import android.annotation.IntDef;
20 import android.annotation.Nullable;
21 import android.app.ActivityManager;
22 import android.app.PendingIntent;
23 import android.bluetooth.BluetoothAdapter;
24 import android.content.BroadcastReceiver;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.content.pm.UserInfo;
29 import android.database.ContentObserver;
30 import android.hardware.SensorPrivacyManager;
31 import android.hardware.SensorPrivacyManagerInternal;
32 import android.hardware.location.ContextHubInfo;
33 import android.hardware.location.ContextHubMessage;
34 import android.hardware.location.ContextHubTransaction;
35 import android.hardware.location.IContextHubCallback;
36 import android.hardware.location.IContextHubClient;
37 import android.hardware.location.IContextHubClientCallback;
38 import android.hardware.location.IContextHubService;
39 import android.hardware.location.IContextHubTransactionCallback;
40 import android.hardware.location.NanoApp;
41 import android.hardware.location.NanoAppBinary;
42 import android.hardware.location.NanoAppFilter;
43 import android.hardware.location.NanoAppInstanceInfo;
44 import android.hardware.location.NanoAppMessage;
45 import android.hardware.location.NanoAppState;
46 import android.location.LocationManager;
47 import android.net.wifi.WifiManager;
48 import android.os.Binder;
49 import android.os.RemoteCallbackList;
50 import android.os.RemoteException;
51 import android.os.ResultReceiver;
52 import android.os.ShellCallback;
53 import android.os.SystemClock;
54 import android.os.UserHandle;
55 import android.os.UserManager;
56 import android.provider.Settings;
57 import android.util.Log;
58 import android.util.Pair;
59 import android.util.proto.ProtoOutputStream;
60 
61 import com.android.internal.util.DumpUtils;
62 import com.android.server.LocalServices;
63 import com.android.server.location.ContextHubServiceProto;
64 
65 import java.io.FileDescriptor;
66 import java.io.PrintWriter;
67 import java.lang.annotation.Retention;
68 import java.lang.annotation.RetentionPolicy;
69 import java.nio.ByteBuffer;
70 import java.nio.ByteOrder;
71 import java.util.ArrayList;
72 import java.util.Arrays;
73 import java.util.Collections;
74 import java.util.HashMap;
75 import java.util.List;
76 import java.util.Map;
77 import java.util.Objects;
78 import java.util.Set;
79 import java.util.concurrent.ConcurrentHashMap;
80 import java.util.concurrent.ScheduledThreadPoolExecutor;
81 import java.util.concurrent.TimeUnit;
82 import java.util.concurrent.atomic.AtomicLong;
83 
84 /**
85  * @hide
86  */
87 public class ContextHubService extends IContextHubService.Stub {
88     private static final String TAG = "ContextHubService";
89 
90     /*
91      * Constants for the type of transaction that is defined by ContextHubService.
92      * This is used to report the transaction callback to clients, and is different from
93      * ContextHubTransaction.Type.
94      */
95     public static final int MSG_ENABLE_NANO_APP = 1;
96     public static final int MSG_DISABLE_NANO_APP = 2;
97     public static final int MSG_LOAD_NANO_APP = 3;
98     public static final int MSG_UNLOAD_NANO_APP = 4;
99     public static final int MSG_QUERY_NANO_APPS = 5;
100     public static final int MSG_QUERY_MEMORY = 6;
101     public static final int MSG_HUB_RESET = 7;
102 
103     private static final int OS_APP_INSTANCE = -1;
104 
105     /**
106      * Constants describing an async event from the Context Hub.
107      * {@hide}
108      */
109     @Retention(RetentionPolicy.SOURCE)
110     @IntDef(prefix = {"CONTEXT_HUB_EVENT_"}, value = {
111             CONTEXT_HUB_EVENT_UNKNOWN,
112             CONTEXT_HUB_EVENT_RESTARTED,
113     })
114     public @interface Type {
115     }
116 
117     public static final int CONTEXT_HUB_EVENT_UNKNOWN = 0;
118     public static final int CONTEXT_HUB_EVENT_RESTARTED = 1;
119 
120     /*
121      * Local flag to enable debug logging.
122      */
123     private static final boolean DEBUG_LOG_ENABLED = false;
124 
125     private final Context mContext;
126 
127     private Map<Integer, ContextHubInfo> mContextHubIdToInfoMap;
128     private List<String> mSupportedContextHubPerms;
129     private List<ContextHubInfo> mContextHubInfoList;
130     private final RemoteCallbackList<IContextHubCallback> mCallbacksList =
131             new RemoteCallbackList<>();
132 
133     // Proxy object to communicate with the Context Hub HAL
134     private final IContextHubWrapper mContextHubWrapper;
135 
136     // The manager for transaction queue
137     private ContextHubTransactionManager mTransactionManager;
138 
139     // The manager for sending messages to/from clients
140     private ContextHubClientManager mClientManager;
141 
142     // The default client for old API clients
143     private Map<Integer, IContextHubClient> mDefaultClientMap;
144 
145     // The manager for the internal nanoapp state cache
146     private final NanoAppStateManager mNanoAppStateManager = new NanoAppStateManager();
147 
148     // An executor and the future object for scheduling timeout timers
149     private final ScheduledThreadPoolExecutor mDailyMetricTimer =
150             new ScheduledThreadPoolExecutor(1);
151 
152     // The period of the recurring time
153     private static final int PERIOD_METRIC_QUERY_DAYS = 1;
154 
155     // True if WiFi is available for the Context Hub
156     private boolean mIsWifiAvailable = false;
157     private boolean mIsWifiScanningEnabled = false;
158     private boolean mIsWifiMainEnabled = false;
159 
160     // True if BT is available for the Context Hub
161     private boolean mIsBtScanningEnabled = false;
162     private boolean mIsBtMainEnabled = false;
163 
164     // A hashmap used to record if a contexthub is waiting for daily query
165     private Set<Integer> mMetricQueryPendingContextHubIds =
166             Collections.newSetFromMap(new ConcurrentHashMap<Integer, Boolean>());
167 
168     // Lock object for sendWifiSettingUpdate()
169     private final Object mSendWifiSettingUpdateLock = new Object();
170 
171     private SensorPrivacyManagerInternal mSensorPrivacyManagerInternal;
172 
173     private UserManager mUserManager = null;
174 
175     private final Map<Integer, AtomicLong> mLastRestartTimestampMap = new HashMap<>();
176 
177     /**
178      * Class extending the callback to register with a Context Hub.
179      */
180     private class ContextHubServiceCallback implements IContextHubWrapper.ICallback {
181         private final int mContextHubId;
182 
ContextHubServiceCallback(int contextHubId)183         ContextHubServiceCallback(int contextHubId) {
184             mContextHubId = contextHubId;
185         }
186 
187         @Override
handleTransactionResult(int transactionId, boolean success)188         public void handleTransactionResult(int transactionId, boolean success) {
189             handleTransactionResultCallback(mContextHubId, transactionId, success);
190         }
191 
192         @Override
handleContextHubEvent(int eventType)193         public void handleContextHubEvent(int eventType) {
194             handleHubEventCallback(mContextHubId, eventType);
195         }
196 
197         @Override
handleNanoappAbort(long nanoappId, int abortCode)198         public void handleNanoappAbort(long nanoappId, int abortCode) {
199             handleAppAbortCallback(mContextHubId, nanoappId, abortCode);
200         }
201 
202         @Override
handleNanoappInfo(List<NanoAppState> nanoappStateList)203         public void handleNanoappInfo(List<NanoAppState> nanoappStateList) {
204             handleQueryAppsCallback(mContextHubId, nanoappStateList);
205         }
206 
207         @Override
handleNanoappMessage(short hostEndpointId, NanoAppMessage message, List<String> nanoappPermissions, List<String> messagePermissions)208         public void handleNanoappMessage(short hostEndpointId, NanoAppMessage message,
209                 List<String> nanoappPermissions, List<String> messagePermissions) {
210             handleClientMessageCallback(mContextHubId, hostEndpointId, message, nanoappPermissions,
211                     messagePermissions);
212         }
213 
214         @Override
handleServiceRestart()215         public void handleServiceRestart() {
216             Log.i(TAG, "Starting Context Hub Service restart");
217             initExistingCallbacks();
218             resetSettings();
219             Log.i(TAG, "Finished Context Hub Service restart");
220         }
221     }
222 
ContextHubService(Context context, IContextHubWrapper contextHubWrapper)223     public ContextHubService(Context context, IContextHubWrapper contextHubWrapper) {
224         Log.i(TAG, "Starting Context Hub Service init");
225         mContext = context;
226         long startTimeNs = SystemClock.elapsedRealtimeNanos();
227         mContextHubWrapper = contextHubWrapper;
228         if (!initContextHubServiceState(startTimeNs)) {
229             Log.e(TAG, "Failed to initialize the Context Hub Service");
230             return;
231         }
232         initDefaultClientMap();
233 
234         initLocationSettingNotifications();
235         initWifiSettingNotifications();
236         initAirplaneModeSettingNotifications();
237         initMicrophoneSettingNotifications();
238         initBtSettingNotifications();
239 
240         scheduleDailyMetricSnapshot();
241         Log.i(TAG, "Finished Context Hub Service init");
242     }
243 
244     /**
245      * Creates a default client callback for old API clients.
246      *
247      * @param contextHubId the ID of the hub to attach this client to
248      * @return the internal callback interface
249      */
createDefaultClientCallback(int contextHubId)250     private IContextHubClientCallback createDefaultClientCallback(int contextHubId) {
251         return new IContextHubClientCallback.Stub() {
252             private void finishCallback() {
253                 try {
254                     IContextHubClient client = mDefaultClientMap.get(contextHubId);
255                     client.callbackFinished();
256                 } catch (RemoteException e) {
257                     Log.e(
258                             TAG,
259                             "RemoteException while finishing callback for hub (ID = "
260                                     + contextHubId
261                                     + ")",
262                             e);
263                 }
264             }
265 
266             @Override
267             public void onMessageFromNanoApp(NanoAppMessage message) {
268                 int nanoAppHandle =
269                         mNanoAppStateManager.getNanoAppHandle(contextHubId, message.getNanoAppId());
270 
271                 onMessageReceiptOldApi(
272                         message.getMessageType(),
273                         contextHubId,
274                         nanoAppHandle,
275                         message.getMessageBody());
276 
277                 finishCallback();
278             }
279 
280             @Override
281             public void onHubReset() {
282                 byte[] data = {android.hardware.contexthub.V1_0.TransactionResult.SUCCESS};
283                 onMessageReceiptOldApi(MSG_HUB_RESET, contextHubId, OS_APP_INSTANCE, data);
284                 finishCallback();
285             }
286 
287             @Override
288             public void onNanoAppAborted(long nanoAppId, int abortCode) {
289                 finishCallback();
290             }
291 
292             @Override
293             public void onNanoAppLoaded(long nanoAppId) {
294                 finishCallback();
295             }
296 
297             @Override
298             public void onNanoAppUnloaded(long nanoAppId) {
299                 finishCallback();
300             }
301 
302             @Override
303             public void onNanoAppEnabled(long nanoAppId) {
304                 finishCallback();
305             }
306 
307             @Override
308             public void onNanoAppDisabled(long nanoAppId) {
309                 finishCallback();
310             }
311 
312             @Override
313             public void onClientAuthorizationChanged(long nanoAppId, int authorization) {
314                 finishCallback();
315             }
316         };
317     }
318 
319     /**
320      * Initializes the private state of the ContextHubService
321      *
322      * @param startTimeNs               the start time when init was called
323      *
324      * @return      if mContextHubWrapper is not null and a full state init was done
325      */
326     private boolean initContextHubServiceState(long startTimeNs) {
327         if (mContextHubWrapper == null) {
328             mTransactionManager = null;
329             mClientManager = null;
330             mSensorPrivacyManagerInternal = null;
331             mDefaultClientMap = Collections.emptyMap();
332             mContextHubIdToInfoMap = Collections.emptyMap();
333             mSupportedContextHubPerms = Collections.emptyList();
334             mContextHubInfoList = Collections.emptyList();
335             return false;
336         }
337 
338         Pair<List<ContextHubInfo>, List<String>> hubInfo;
339         try {
340             hubInfo = mContextHubWrapper.getHubs();
341         } catch (RemoteException e) {
342             Log.e(TAG, "RemoteException while getting Context Hub info", e);
343             hubInfo = new Pair(Collections.emptyList(), Collections.emptyList());
344         }
345 
346         long bootTimeNs = SystemClock.elapsedRealtimeNanos() - startTimeNs;
347         int numContextHubs = hubInfo.first.size();
348         ContextHubStatsLog.write(ContextHubStatsLog.CONTEXT_HUB_BOOTED, bootTimeNs,
349                 numContextHubs);
350 
351         mContextHubIdToInfoMap = Collections.unmodifiableMap(
352                 ContextHubServiceUtil.createContextHubInfoMap(hubInfo.first));
353         mSupportedContextHubPerms = hubInfo.second;
354         mContextHubInfoList = new ArrayList<>(mContextHubIdToInfoMap.values());
355         mClientManager = new ContextHubClientManager(mContext, mContextHubWrapper);
356         mTransactionManager = new ContextHubTransactionManager(
357                 mContextHubWrapper, mClientManager, mNanoAppStateManager);
358         mSensorPrivacyManagerInternal =
359                 LocalServices.getService(SensorPrivacyManagerInternal.class);
360         return true;
361     }
362 
363     /**
364      * Creates the default client map that maps context hub IDs to the associated
365      * ClientManager. The client map is unmodifiable
366      */
367     private void initDefaultClientMap() {
368         HashMap<Integer, IContextHubClient> defaultClientMap = new HashMap<>();
369         for (Map.Entry<Integer, ContextHubInfo> entry: mContextHubIdToInfoMap.entrySet()) {
370             int contextHubId = entry.getKey();
371             ContextHubInfo contextHubInfo = entry.getValue();
372 
373             mLastRestartTimestampMap.put(contextHubId,
374                     new AtomicLong(SystemClock.elapsedRealtimeNanos()));
375 
376             try {
377                 mContextHubWrapper.registerCallback(contextHubId,
378                         new ContextHubServiceCallback(contextHubId));
379             } catch (RemoteException e) {
380                 Log.e(TAG, "RemoteException while registering service callback for hub (ID = "
381                         + contextHubId + ")", e);
382             }
383 
384             IContextHubClient client = mClientManager.registerClient(
385                     contextHubInfo, createDefaultClientCallback(contextHubId),
386                     /* attributionTag= */ null, mTransactionManager, mContext.getPackageName());
387             defaultClientMap.put(contextHubId, client);
388 
389             // Do a query to initialize the service cache list of nanoapps
390             // TODO(b/194289715): Remove this when old API is deprecated
391             queryNanoAppsInternal(contextHubId);
392         }
393         mDefaultClientMap = Collections.unmodifiableMap(defaultClientMap);
394     }
395 
396     /**
397      * Initializes existing callbacks with the mContextHubWrapper for every context hub
398      */
399     private void initExistingCallbacks() {
400         for (int contextHubId : mContextHubIdToInfoMap.keySet()) {
401             try {
402                 mContextHubWrapper.registerExistingCallback(contextHubId);
403             } catch (RemoteException e) {
404                 Log.e(TAG, "RemoteException while registering existing service callback for hub "
405                         + "(ID = " + contextHubId + ")", e);
406             }
407         }
408     }
409 
410     /**
411      * Handles the initialization of location settings notifications
412      */
413     private void initLocationSettingNotifications() {
414         if (mContextHubWrapper == null
415                 || !mContextHubWrapper.supportsLocationSettingNotifications()) {
416             return;
417         }
418 
419         sendLocationSettingUpdate();
420         mContext.getContentResolver().registerContentObserver(
421                 Settings.Secure.getUriFor(Settings.Secure.LOCATION_MODE),
422                 /* notifyForDescendants= */ true,
423                 new ContentObserver(/* handler= */ null) {
424                     @Override
425                     public void onChange(boolean selfChange) {
426                         sendLocationSettingUpdate();
427                     }
428                 }, UserHandle.USER_ALL);
429     }
430 
431     /**
432      * Handles the initialization of wifi settings notifications
433      */
434     private void initWifiSettingNotifications() {
435         if (mContextHubWrapper == null || !mContextHubWrapper.supportsWifiSettingNotifications()) {
436             return;
437         }
438 
439         sendWifiSettingUpdate(/* forceUpdate= */ true);
440 
441         BroadcastReceiver wifiReceiver = new BroadcastReceiver() {
442             @Override
443             public void onReceive(Context context, Intent intent) {
444                 if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(intent.getAction())
445                         || WifiManager.ACTION_WIFI_SCAN_AVAILABILITY_CHANGED.equals(
446                         intent.getAction())) {
447                     sendWifiSettingUpdate(/* forceUpdate= */ false);
448                 }
449             }
450         };
451         IntentFilter filter = new IntentFilter();
452         filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
453         filter.addAction(WifiManager.ACTION_WIFI_SCAN_AVAILABILITY_CHANGED);
454         mContext.registerReceiver(wifiReceiver, filter);
455 
456         mContext.getContentResolver().registerContentObserver(
457                 Settings.Global.getUriFor(Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE),
458                 /* notifyForDescendants= */ true,
459                 new ContentObserver(/* handler= */ null) {
460                     @Override
461                     public void onChange(boolean selfChange) {
462                         sendWifiSettingUpdate(/* forceUpdate= */ false);
463                     }
464                 }, UserHandle.USER_ALL);
465     }
466 
467     /**
468      * Handles the initialization of airplane mode settings notifications
469      */
470     private void initAirplaneModeSettingNotifications() {
471         if (mContextHubWrapper == null
472                 || !mContextHubWrapper.supportsAirplaneModeSettingNotifications()) {
473             return;
474         }
475 
476         sendAirplaneModeSettingUpdate();
477         mContext.getContentResolver().registerContentObserver(
478                 Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON),
479                 /* notifyForDescendants= */ true,
480                 new ContentObserver(/* handler= */ null) {
481                     @Override
482                     public void onChange(boolean selfChange) {
483                         sendAirplaneModeSettingUpdate();
484                     }
485                 }, UserHandle.USER_ALL);
486     }
487 
488     /**
489      * Handles the initialization of microphone settings notifications
490      */
491     private void initMicrophoneSettingNotifications() {
492         if (mContextHubWrapper == null
493                 || !mContextHubWrapper.supportsMicrophoneSettingNotifications()) {
494             return;
495         }
496 
497         if (mUserManager == null) {
498             mUserManager = mContext.getSystemService(UserManager.class);
499             if (mUserManager == null) {
500                 Log.e(TAG, "Unable to get the UserManager service");
501                 return;
502             }
503         }
504 
505         sendMicrophoneDisableSettingUpdateForCurrentUser();
506         if (mSensorPrivacyManagerInternal == null) {
507             Log.e(TAG, "Unable to add a sensor privacy listener for all users");
508             return;
509         }
510 
511         mSensorPrivacyManagerInternal.addSensorPrivacyListenerForAllUsers(
512                 SensorPrivacyManager.Sensors.MICROPHONE, (userId, enabled) -> {
513                     // If we are in HSUM mode, any user can change the microphone setting
514                     if (mUserManager.isHeadlessSystemUserMode() || userId == getCurrentUserId()) {
515                         Log.d(TAG, "User: " + userId + " mic privacy: " + enabled);
516                         sendMicrophoneDisableSettingUpdate(enabled);
517                     }
518                 });
519     }
520 
521     /**
522      * Handles the initialization of bluetooth settings notifications
523      */
524     private void initBtSettingNotifications() {
525         if (mContextHubWrapper == null || !mContextHubWrapper.supportsBtSettingNotifications()) {
526             return;
527         }
528 
529         sendBtSettingUpdate(/* forceUpdate= */ true);
530 
531         BroadcastReceiver btReceiver = new BroadcastReceiver() {
532             @Override
533             public void onReceive(Context context, Intent intent) {
534                 if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
535                     sendBtSettingUpdate(/* forceUpdate= */ false);
536                 }
537             }
538         };
539         IntentFilter filter = new IntentFilter();
540         filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
541         mContext.registerReceiver(btReceiver, filter);
542 
543         mContext.getContentResolver().registerContentObserver(
544                 Settings.Global.getUriFor(Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE),
545                 /* notifyForDescendants= */ false,
546                 new ContentObserver(/* handler= */ null) {
547                     @Override
548                     public void onChange(boolean selfChange) {
549                         sendBtSettingUpdate(/* forceUpdate= */ false);
550                     }
551                 }, UserHandle.USER_ALL);
552     }
553 
554     /**
555      * Resets the settings. Called when a context hub restarts or the AIDL HAL dies
556      */
557     private void resetSettings() {
558         sendLocationSettingUpdate();
559         sendWifiSettingUpdate(/* forceUpdate= */ true);
560         sendAirplaneModeSettingUpdate();
561         sendMicrophoneDisableSettingUpdateForCurrentUser();
562         sendBtSettingUpdate(/* forceUpdate= */ true);
563     }
564 
565     @Override
566     public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
567             String[] args, ShellCallback callback, ResultReceiver result) {
568         new ContextHubShellCommand(mContext, this).exec(this, in, out, err, args, callback, result);
569     }
570 
571     @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
572     @Override
573     public int registerCallback(IContextHubCallback callback) throws RemoteException {
574         super.registerCallback_enforcePermission();
575 
576         mCallbacksList.register(callback);
577 
578         Log.d(TAG, "Added callback, total callbacks " +
579                 mCallbacksList.getRegisteredCallbackCount());
580         return 0;
581     }
582 
583     @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
584     @Override
585     public int[] getContextHubHandles() throws RemoteException {
586         super.getContextHubHandles_enforcePermission();
587 
588         return ContextHubServiceUtil.createPrimitiveIntArray(mContextHubIdToInfoMap.keySet());
589     }
590 
591     @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
592     @Override
593     public ContextHubInfo getContextHubInfo(int contextHubHandle) throws RemoteException {
594         super.getContextHubInfo_enforcePermission();
595 
596         if (!mContextHubIdToInfoMap.containsKey(contextHubHandle)) {
597             Log.e(TAG, "Invalid Context Hub handle " + contextHubHandle + " in getContextHubInfo");
598             return null;
599         }
600 
601         return mContextHubIdToInfoMap.get(contextHubHandle);
602     }
603 
604     @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
605     /**
606      * Returns a List of ContextHubInfo object describing the available hubs.
607      *
608      * @return the List of ContextHubInfo objects
609      */
610     @Override
611     public List<ContextHubInfo> getContextHubs() throws RemoteException {
612         super.getContextHubs_enforcePermission();
613 
614         return mContextHubInfoList;
615     }
616 
617     /**
618      * Creates an internal load transaction callback to be used for old API clients
619      *
620      * @param contextHubId  the ID of the hub to load the binary
621      * @param nanoAppBinary the binary to load
622      * @return the callback interface
623      */
624     private IContextHubTransactionCallback createLoadTransactionCallback(
625             int contextHubId, NanoAppBinary nanoAppBinary) {
626         return new IContextHubTransactionCallback.Stub() {
627             @Override
628             public void onTransactionComplete(int result) {
629                 handleLoadResponseOldApi(contextHubId, result, nanoAppBinary);
630             }
631 
632             @Override
633             public void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) {
634             }
635         };
636     }
637 
638     /**
639      * Creates an internal unload transaction callback to be used for old API clients
640      *
641      * @param contextHubId the ID of the hub to unload the nanoapp
642      * @return the callback interface
643      */
644     private IContextHubTransactionCallback createUnloadTransactionCallback(int contextHubId) {
645         return new IContextHubTransactionCallback.Stub() {
646             @Override
647             public void onTransactionComplete(int result) {
648                 handleUnloadResponseOldApi(contextHubId, result);
649             }
650 
651             @Override
652             public void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) {
653             }
654         };
655     }
656 
657     /**
658      * Creates an internal query transaction callback to be used for old API clients
659      *
660      * @param contextHubId the ID of the hub to query
661      * @return the callback interface
662      */
663     private IContextHubTransactionCallback createQueryTransactionCallback(int contextHubId) {
664         return new IContextHubTransactionCallback.Stub() {
665             @Override
666             public void onTransactionComplete(int result) {
667             }
668 
669             @Override
670             public void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) {
671                 byte[] data = {(byte) result};
672                 onMessageReceiptOldApi(MSG_QUERY_NANO_APPS, contextHubId, OS_APP_INSTANCE, data);
673             }
674         };
675     }
676 
677     @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
678     @Override
679     public int loadNanoApp(int contextHubHandle, NanoApp nanoApp) throws RemoteException {
680         super.loadNanoApp_enforcePermission();
681 
682         if (mContextHubWrapper == null) {
683             return -1;
684         }
685         if (!isValidContextHubId(contextHubHandle)) {
686             Log.e(TAG, "Invalid Context Hub handle " + contextHubHandle + " in loadNanoApp");
687             return -1;
688         }
689         if (nanoApp == null) {
690             Log.e(TAG, "NanoApp cannot be null in loadNanoApp");
691             return -1;
692         }
693 
694         // Create an internal IContextHubTransactionCallback for the old API clients
695         NanoAppBinary nanoAppBinary = new NanoAppBinary(nanoApp.getAppBinary());
696         IContextHubTransactionCallback onCompleteCallback =
697                 createLoadTransactionCallback(contextHubHandle, nanoAppBinary);
698 
699         ContextHubServiceTransaction transaction = mTransactionManager.createLoadTransaction(
700                 contextHubHandle, nanoAppBinary, onCompleteCallback, getCallingPackageName());
701 
702         mTransactionManager.addTransaction(transaction);
703         return 0;
704     }
705 
706     @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
707     @Override
708     public int unloadNanoApp(int nanoAppHandle) throws RemoteException {
709         super.unloadNanoApp_enforcePermission();
710 
711         if (mContextHubWrapper == null) {
712             return -1;
713         }
714 
715         NanoAppInstanceInfo info =
716                 mNanoAppStateManager.getNanoAppInstanceInfo(nanoAppHandle);
717         if (info == null) {
718             Log.e(TAG, "Invalid nanoapp handle " + nanoAppHandle + " in unloadNanoApp");
719             return -1;
720         }
721 
722         int contextHubId = info.getContexthubId();
723         long nanoAppId = info.getAppId();
724         IContextHubTransactionCallback onCompleteCallback =
725                 createUnloadTransactionCallback(contextHubId);
726         ContextHubServiceTransaction transaction = mTransactionManager.createUnloadTransaction(
727                 contextHubId, nanoAppId, onCompleteCallback, getCallingPackageName());
728 
729         mTransactionManager.addTransaction(transaction);
730         return 0;
731     }
732 
733     @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
734     @Override
735     public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppHandle) throws RemoteException {
736 
737         super.getNanoAppInstanceInfo_enforcePermission();
738 
739         return mNanoAppStateManager.getNanoAppInstanceInfo(nanoAppHandle);
740     }
741 
742     @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
743     @Override
744     public int[] findNanoAppOnHub(
745             int contextHubHandle, NanoAppFilter filter) throws RemoteException {
746 
747         super.findNanoAppOnHub_enforcePermission();
748 
749         ArrayList<Integer> foundInstances = new ArrayList<>();
750         if (filter != null) {
751             mNanoAppStateManager.foreachNanoAppInstanceInfo((info) -> {
752                 if (filter.testMatch(info)) {
753                     foundInstances.add(info.getHandle());
754                 }
755             });
756         }
757 
758         int[] retArray = new int[foundInstances.size()];
759         for (int i = 0; i < foundInstances.size(); i++) {
760             retArray[i] = foundInstances.get(i).intValue();
761         }
762         return retArray;
763     }
764 
765     /**
766      * Performs a query at the specified hub.
767      * <p>
768      * This method should only be invoked internally by the service, either to update the service
769      * cache or as a result of an explicit query requested by a client through the sendMessage API.
770      *
771      * @param contextHubId the ID of the hub to do the query
772      * @return true if the query succeeded
773      * @throws IllegalStateException if the transaction queue is full
774      */
775     private boolean queryNanoAppsInternal(int contextHubId) {
776         if (mContextHubWrapper == null) {
777             return false;
778         }
779 
780         IContextHubTransactionCallback onCompleteCallback =
781                 createQueryTransactionCallback(contextHubId);
782         ContextHubServiceTransaction transaction = mTransactionManager.createQueryTransaction(
783                 contextHubId, onCompleteCallback, getCallingPackageName());
784 
785         mTransactionManager.addTransaction(transaction);
786         return true;
787     }
788 
789     @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
790     @Override
791     public int sendMessage(int contextHubHandle, int nanoAppHandle, ContextHubMessage msg)
792             throws RemoteException {
793         super.sendMessage_enforcePermission();
794 
795         if (mContextHubWrapper == null) {
796             return -1;
797         }
798         if (msg == null) {
799             Log.e(TAG, "ContextHubMessage cannot be null in sendMessage");
800             return -1;
801         }
802         if (msg.getData() == null) {
803             Log.e(TAG, "ContextHubMessage message body cannot be null in sendMessage");
804             return -1;
805         }
806         if (!isValidContextHubId(contextHubHandle)) {
807             Log.e(TAG, "Invalid Context Hub handle " + contextHubHandle + " in sendMessage");
808             return -1;
809         }
810 
811         boolean success = false;
812         if (nanoAppHandle == OS_APP_INSTANCE) {
813             if (msg.getMsgType() == MSG_QUERY_NANO_APPS) {
814                 success = queryNanoAppsInternal(contextHubHandle);
815             } else {
816                 Log.e(TAG, "Invalid OS message params of type " + msg.getMsgType());
817             }
818         } else {
819             NanoAppInstanceInfo info = getNanoAppInstanceInfo(nanoAppHandle);
820             if (info != null) {
821                 NanoAppMessage message = NanoAppMessage.createMessageToNanoApp(
822                         info.getAppId(), msg.getMsgType(), msg.getData());
823 
824                 IContextHubClient client = mDefaultClientMap.get(contextHubHandle);
825                 success = (client.sendMessageToNanoApp(message) ==
826                         ContextHubTransaction.RESULT_SUCCESS);
827             } else {
828                 Log.e(TAG, "Failed to send nanoapp message - nanoapp with handle "
829                         + nanoAppHandle + " does not exist.");
830             }
831         }
832 
833         return success ? 0 : -1;
834     }
835 
836     /**
837      * Handles a unicast or broadcast message from a nanoapp.
838      *
839      * @param contextHubId the ID of the hub the message came from
840      * @param hostEndpointId the host endpoint ID of the client receiving this message
841      * @param message the message contents
842      * @param nanoappPermissions the set of permissions the nanoapp holds
843      * @param messagePermissions the set of permissions that should be used for attributing
844      *     permissions when this message is consumed by a client
845      */
846     private void handleClientMessageCallback(
847             int contextHubId,
848             short hostEndpointId,
849             NanoAppMessage message,
850             List<String> nanoappPermissions,
851             List<String> messagePermissions) {
852         mClientManager.onMessageFromNanoApp(
853                 contextHubId, hostEndpointId, message, nanoappPermissions, messagePermissions);
854     }
855 
856     /**
857      * A helper function to handle a load response from the Context Hub for the old API.
858      * TODO(b/194289715): Remove this once the old APIs are obsolete.
859      */
860     private void handleLoadResponseOldApi(
861             int contextHubId, int result, NanoAppBinary nanoAppBinary) {
862         if (nanoAppBinary == null) {
863             Log.e(TAG, "Nanoapp binary field was null for a load transaction");
864             return;
865         }
866 
867         byte[] data = new byte[5];
868         data[0] = (byte) result;
869         int nanoAppHandle = mNanoAppStateManager.getNanoAppHandle(
870                 contextHubId, nanoAppBinary.getNanoAppId());
871         ByteBuffer.wrap(data, 1, 4).order(ByteOrder.nativeOrder()).putInt(nanoAppHandle);
872 
873         onMessageReceiptOldApi(MSG_LOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data);
874     }
875 
876     /**
877      * A helper function to handle an unload response from the Context Hub for the old API.
878      * <p>
879      * TODO(b/194289715): Remove this once the old APIs are obsolete.
880      */
881     private void handleUnloadResponseOldApi(int contextHubId, int result) {
882         byte[] data = new byte[1];
883         data[0] = (byte) result;
884         onMessageReceiptOldApi(MSG_UNLOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data);
885     }
886 
887     /**
888      * Handles a transaction response from a Context Hub.
889      *
890      * @param contextHubId  the ID of the hub the response came from
891      * @param transactionId the ID of the transaction
892      * @param success       true if the transaction succeeded
893      */
894     private void handleTransactionResultCallback(int contextHubId, int transactionId,
895             boolean success) {
896         mTransactionManager.onTransactionResponse(transactionId, success);
897     }
898 
899     /**
900      * Handles an asynchronous event from a Context Hub.
901      *
902      * @param contextHubId the ID of the hub the response came from
903      * @param eventType    the type of the event as in CONTEXT_HUB_EVENT_*
904      */
905     private void handleHubEventCallback(int contextHubId, int eventType) {
906         if (eventType == CONTEXT_HUB_EVENT_RESTARTED) {
907             long now = SystemClock.elapsedRealtimeNanos();
908             long lastRestartTimeNs = mLastRestartTimestampMap.get(contextHubId).getAndSet(now);
909             ContextHubStatsLog.write(
910                     ContextHubStatsLog.CONTEXT_HUB_RESTARTED,
911                     TimeUnit.NANOSECONDS.toMillis(now - lastRestartTimeNs),
912                     contextHubId);
913 
914             ContextHubEventLogger.getInstance().logContextHubRestart(contextHubId);
915 
916             resetSettings();
917 
918             mTransactionManager.onHubReset();
919             queryNanoAppsInternal(contextHubId);
920 
921             mClientManager.onHubReset(contextHubId);
922         } else {
923             Log.i(TAG, "Received unknown hub event (hub ID = " + contextHubId + ", type = "
924                     + eventType + ")");
925         }
926     }
927 
928     /**
929      * Handles an asynchronous abort event of a nanoapp.
930      *
931      * @param contextHubId the ID of the hub that the nanoapp aborted in
932      * @param nanoAppId    the ID of the aborted nanoapp
933      * @param abortCode    the nanoapp-specific abort code
934      */
935     private void handleAppAbortCallback(int contextHubId, long nanoAppId, int abortCode) {
936         mClientManager.onNanoAppAborted(contextHubId, nanoAppId, abortCode);
937     }
938 
939     /**
940      * Handles a query response from a Context Hub.
941      *
942      * @param contextHubId     the ID of the hub of the response
943      * @param nanoappStateList the list of loaded nanoapps
944      */
945     private void handleQueryAppsCallback(int contextHubId, List<NanoAppState> nanoappStateList) {
946         if (mMetricQueryPendingContextHubIds.contains(contextHubId)) {
947             for (NanoAppState nanoappState : nanoappStateList) {
948                 ContextHubStatsLog.write(
949                         ContextHubStatsLog.CONTEXT_HUB_LOADED_NANOAPP_SNAPSHOT_REPORTED,
950                         contextHubId, nanoappState.getNanoAppId(),
951                         (int) nanoappState.getNanoAppVersion());
952             }
953             mMetricQueryPendingContextHubIds.remove(contextHubId);
954             if (mMetricQueryPendingContextHubIds.isEmpty()) {
955                 scheduleDailyMetricSnapshot();
956             }
957         }
958         mNanoAppStateManager.updateCache(contextHubId, nanoappStateList);
959         mTransactionManager.onQueryResponse(nanoappStateList);
960     }
961 
962     /**
963      * @param contextHubId the hub ID to validate
964      * @return {@code true} if the ID represents that of an available hub, {@code false} otherwise
965      */
966     private boolean isValidContextHubId(int contextHubId) {
967         return mContextHubIdToInfoMap.containsKey(contextHubId);
968     }
969 
970     @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
971     /**
972      * Creates and registers a client at the service for the specified Context Hub.
973      *
974      * @param contextHubId   the ID of the hub this client is attached to
975      * @param clientCallback the client interface to register with the service
976      * @param attributionTag an optional attribution tag within the given package
977      * @param packageName    the name of the package creating this client
978      * @return the generated client interface, null if registration was unsuccessful
979      * @throws IllegalArgumentException if contextHubId is not a valid ID
980      * @throws IllegalStateException    if max number of clients have already registered
981      * @throws NullPointerException     if clientCallback is null
982      */
983     @Override
984     public IContextHubClient createClient(
985             int contextHubId, IContextHubClientCallback clientCallback,
986             @Nullable String attributionTag, String packageName) throws RemoteException {
987         super.createClient_enforcePermission();
988 
989         if (!isValidContextHubId(contextHubId)) {
990             throw new IllegalArgumentException("Invalid context hub ID " + contextHubId);
991         }
992         if (clientCallback == null) {
993             throw new NullPointerException("Cannot register client with null callback");
994         }
995 
996         ContextHubInfo contextHubInfo = mContextHubIdToInfoMap.get(contextHubId);
997         return mClientManager.registerClient(
998                 contextHubInfo, clientCallback, attributionTag, mTransactionManager, packageName);
999     }
1000 
1001     @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
1002     /**
1003      * Creates and registers a PendingIntent client at the service for the specified Context Hub.
1004      *
1005      * @param contextHubId   the ID of the hub this client is attached to
1006      * @param pendingIntent  the PendingIntent associated with this client
1007      * @param nanoAppId      the ID of the nanoapp PendingIntent events will be sent for
1008      * @param attributionTag an optional attribution tag within the given package
1009      * @return the generated client interface
1010      * @throws IllegalArgumentException if hubInfo does not represent a valid hub
1011      * @throws IllegalStateException    if there were too many registered clients at the service
1012      */
1013     @Override
1014     public IContextHubClient createPendingIntentClient(
1015             int contextHubId, PendingIntent pendingIntent, long nanoAppId,
1016             @Nullable String attributionTag) throws RemoteException {
1017         super.createPendingIntentClient_enforcePermission();
1018 
1019         if (!isValidContextHubId(contextHubId)) {
1020             throw new IllegalArgumentException("Invalid context hub ID " + contextHubId);
1021         }
1022 
1023         ContextHubInfo contextHubInfo = mContextHubIdToInfoMap.get(contextHubId);
1024         return mClientManager.registerClient(
1025                 contextHubInfo, pendingIntent, nanoAppId, attributionTag, mTransactionManager);
1026     }
1027 
1028     @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
1029     /**
1030      * Loads a nanoapp binary at the specified Context hub.
1031      *
1032      * @param contextHubId        the ID of the hub to load the binary
1033      * @param transactionCallback the client-facing transaction callback interface
1034      * @param nanoAppBinary       the binary to load
1035      * @throws IllegalStateException if the transaction queue is full
1036      */
1037     @Override
1038     public void loadNanoAppOnHub(
1039             int contextHubId, IContextHubTransactionCallback transactionCallback,
1040             NanoAppBinary nanoAppBinary) throws RemoteException {
1041         super.loadNanoAppOnHub_enforcePermission();
1042 
1043         if (!checkHalProxyAndContextHubId(
1044                 contextHubId, transactionCallback, ContextHubTransaction.TYPE_LOAD_NANOAPP)) {
1045             return;
1046         }
1047         if (nanoAppBinary == null) {
1048             Log.e(TAG, "NanoAppBinary cannot be null in loadNanoAppOnHub");
1049             transactionCallback.onTransactionComplete(
1050                     ContextHubTransaction.RESULT_FAILED_BAD_PARAMS);
1051             return;
1052         }
1053 
1054         ContextHubServiceTransaction transaction = mTransactionManager.createLoadTransaction(
1055                 contextHubId, nanoAppBinary, transactionCallback, getCallingPackageName());
1056         mTransactionManager.addTransaction(transaction);
1057     }
1058 
1059     @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
1060     /**
1061      * Unloads a nanoapp from the specified Context Hub.
1062      *
1063      * @param contextHubId        the ID of the hub to unload the nanoapp
1064      * @param transactionCallback the client-facing transaction callback interface
1065      * @param nanoAppId           the ID of the nanoapp to unload
1066      * @throws IllegalStateException if the transaction queue is full
1067      */
1068     @Override
1069     public void unloadNanoAppFromHub(
1070             int contextHubId, IContextHubTransactionCallback transactionCallback, long nanoAppId)
1071             throws RemoteException {
1072         super.unloadNanoAppFromHub_enforcePermission();
1073 
1074         if (!checkHalProxyAndContextHubId(
1075                 contextHubId, transactionCallback, ContextHubTransaction.TYPE_UNLOAD_NANOAPP)) {
1076             return;
1077         }
1078 
1079         ContextHubServiceTransaction transaction = mTransactionManager.createUnloadTransaction(
1080                 contextHubId, nanoAppId, transactionCallback, getCallingPackageName());
1081         mTransactionManager.addTransaction(transaction);
1082     }
1083 
1084     @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
1085     /**
1086      * Enables a nanoapp at the specified Context Hub.
1087      *
1088      * @param contextHubId        the ID of the hub to enable the nanoapp
1089      * @param transactionCallback the client-facing transaction callback interface
1090      * @param nanoAppId           the ID of the nanoapp to enable
1091      * @throws IllegalStateException if the transaction queue is full
1092      */
1093     @Override
1094     public void enableNanoApp(
1095             int contextHubId, IContextHubTransactionCallback transactionCallback, long nanoAppId)
1096             throws RemoteException {
1097         super.enableNanoApp_enforcePermission();
1098 
1099         if (!checkHalProxyAndContextHubId(
1100                 contextHubId, transactionCallback, ContextHubTransaction.TYPE_ENABLE_NANOAPP)) {
1101             return;
1102         }
1103 
1104         ContextHubServiceTransaction transaction = mTransactionManager.createEnableTransaction(
1105                 contextHubId, nanoAppId, transactionCallback, getCallingPackageName());
1106         mTransactionManager.addTransaction(transaction);
1107     }
1108 
1109     @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
1110     /**
1111      * Disables a nanoapp at the specified Context Hub.
1112      *
1113      * @param contextHubId        the ID of the hub to disable the nanoapp
1114      * @param transactionCallback the client-facing transaction callback interface
1115      * @param nanoAppId           the ID of the nanoapp to disable
1116      * @throws IllegalStateException if the transaction queue is full
1117      */
1118     @Override
1119     public void disableNanoApp(
1120             int contextHubId, IContextHubTransactionCallback transactionCallback, long nanoAppId)
1121             throws RemoteException {
1122         super.disableNanoApp_enforcePermission();
1123 
1124         if (!checkHalProxyAndContextHubId(
1125                 contextHubId, transactionCallback, ContextHubTransaction.TYPE_DISABLE_NANOAPP)) {
1126             return;
1127         }
1128 
1129         ContextHubServiceTransaction transaction = mTransactionManager.createDisableTransaction(
1130                 contextHubId, nanoAppId, transactionCallback, getCallingPackageName());
1131         mTransactionManager.addTransaction(transaction);
1132     }
1133 
1134     @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
1135     /**
1136      * Queries for a list of nanoapps from the specified Context hub.
1137      *
1138      * @param contextHubId        the ID of the hub to query
1139      * @param transactionCallback the client-facing transaction callback interface
1140      * @throws IllegalStateException if the transaction queue is full
1141      */
1142     @Override
1143     public void queryNanoApps(int contextHubId, IContextHubTransactionCallback transactionCallback)
1144             throws RemoteException {
1145         super.queryNanoApps_enforcePermission();
1146 
1147         if (!checkHalProxyAndContextHubId(
1148                 contextHubId, transactionCallback, ContextHubTransaction.TYPE_QUERY_NANOAPPS)) {
1149             return;
1150         }
1151 
1152         ContextHubServiceTransaction transaction = mTransactionManager.createQueryTransaction(
1153                 contextHubId, transactionCallback, getCallingPackageName());
1154         mTransactionManager.addTransaction(transaction);
1155     }
1156 
1157     @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
1158     /**
1159      * Queries for a list of preloaded nanoapp IDs from the specified Context Hub.
1160      *
1161      * @param hubInfo The Context Hub to query a list of nanoapps from.
1162      * @return The list of 64-bit IDs of the preloaded nanoapps.
1163      * @throws NullPointerException if hubInfo is null
1164      */
1165     @Override
1166     public long[] getPreloadedNanoAppIds(ContextHubInfo hubInfo) throws RemoteException {
1167         super.getPreloadedNanoAppIds_enforcePermission();
1168         Objects.requireNonNull(hubInfo, "hubInfo cannot be null");
1169 
1170         long[] nanoappIds = mContextHubWrapper.getPreloadedNanoappIds(hubInfo.getId());
1171         if (nanoappIds == null) {
1172             return new long[0];
1173         }
1174         return nanoappIds;
1175     }
1176 
1177     @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
1178     /**
1179      * Puts the context hub in and out of test mode. Test mode is a clean state
1180      * where tests can be executed in the same environment. If enable is true,
1181      * this will enable test mode by unloading all nanoapps. If enable is false,
1182      * this will disable test mode and reverse the actions of enabling test mode
1183      * by loading all preloaded nanoapps. This puts CHRE in a normal state.
1184      *
1185      * This should only be used for a test environment, either through a
1186      * @TestApi or development tools. This should not be used in a production
1187      * environment.
1188      *
1189      * @param enable If true, put the context hub in test mode. If false, disable
1190      *               test mode.
1191      * @return       If true, the operation was successful; false otherwise.
1192      */
1193     @Override
1194     public boolean setTestMode(boolean enable) {
1195         super.setTestMode_enforcePermission();
1196         boolean status = mContextHubWrapper.setTestMode(enable);
1197 
1198         // Query nanoapps to update service state after test mode state change.
1199         for (int contextHubId: mDefaultClientMap.keySet()) {
1200             queryNanoAppsInternal(contextHubId);
1201         }
1202         return status;
1203     }
1204 
1205     @Override
1206     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1207         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
1208 
1209         for (String arg : args) {
1210             if ("--proto".equals(arg)) {
1211                 dump(new ProtoOutputStream(fd));
1212                 return;
1213             }
1214         }
1215 
1216         pw.println("Dumping ContextHub Service");
1217 
1218         pw.println("");
1219         // dump ContextHubInfo
1220         pw.println("=================== CONTEXT HUBS ====================");
1221         for (ContextHubInfo hubInfo : mContextHubIdToInfoMap.values()) {
1222             pw.println(hubInfo);
1223         }
1224         pw.println("Supported permissions: "
1225                 + Arrays.toString(mSupportedContextHubPerms.toArray()));
1226         pw.println("");
1227         pw.println("=================== NANOAPPS ====================");
1228         // Dump nanoAppHash
1229         mNanoAppStateManager.foreachNanoAppInstanceInfo(pw::println);
1230 
1231         pw.println("");
1232         pw.println("=================== PRELOADED NANOAPPS ====================");
1233         dumpPreloadedNanoapps(pw);
1234 
1235         pw.println("");
1236         pw.println("=================== CLIENTS ====================");
1237         pw.println(mClientManager);
1238 
1239         pw.println("");
1240         pw.println("=================== TRANSACTIONS ====================");
1241         pw.println(mTransactionManager);
1242 
1243         pw.println("");
1244         pw.println("=================== EVENTS ====================");
1245         pw.println(ContextHubEventLogger.getInstance());
1246 
1247         // dump eventLog
1248     }
1249 
1250     /* package */ void denyClientAuthState(int contextHubId, String packageName, long nanoAppId) {
1251         Log.i(TAG, "Denying " + packageName + " access to " + Long.toHexString(nanoAppId)
1252                 + " on context hub # " + contextHubId);
1253 
1254         mClientManager.forEachClientOfHub(contextHubId, client -> {
1255             if (client.getPackageName().equals(packageName)) {
1256                 client.updateNanoAppAuthState(
1257                         nanoAppId, /* nanoappPermissions= */ Collections.emptyList(),
1258                         /* gracePeriodExpired= */ false, /* forceDenied= */ true);
1259             }
1260         });
1261     }
1262 
1263     private void dump(ProtoOutputStream proto) {
1264         mContextHubIdToInfoMap.values().forEach(hubInfo -> {
1265             long token = proto.start(ContextHubServiceProto.CONTEXT_HUB_INFO);
1266             hubInfo.dump(proto);
1267             proto.end(token);
1268         });
1269 
1270         long token = proto.start(ContextHubServiceProto.CLIENT_MANAGER);
1271         mClientManager.dump(proto);
1272         proto.end(token);
1273 
1274         proto.flush();
1275     }
1276 
1277     /** Dumps preloaded nanoapps to the console */
1278     private void dumpPreloadedNanoapps(PrintWriter pw) {
1279         if (mContextHubWrapper == null) {
1280             return;
1281         }
1282 
1283         for (int contextHubId: mContextHubIdToInfoMap.keySet()) {
1284             long[] preloadedNanoappIds = mContextHubWrapper.getPreloadedNanoappIds(contextHubId);
1285             if (preloadedNanoappIds == null) {
1286                 return;
1287             }
1288 
1289             pw.print("Context Hub (id=");
1290             pw.print(contextHubId);
1291             pw.println("):");
1292             for (long preloadedNanoappId : preloadedNanoappIds) {
1293                 pw.print("  ID: 0x");
1294                 pw.println(Long.toHexString(preloadedNanoappId));
1295             }
1296         }
1297     }
1298 
1299     private void checkPermissions() {
1300         ContextHubServiceUtil.checkPermissions(mContext);
1301     }
1302 
1303     private int onMessageReceiptOldApi(
1304             int msgType, int contextHubHandle, int appInstance, byte[] data) {
1305         if (data == null) {
1306             return -1;
1307         }
1308 
1309         int msgVersion = 0;
1310         // Synchronize access to mCallbacksList to prevent more than one outstanding broadcast as
1311         // that will cause a crash.
1312         synchronized (mCallbacksList) {
1313             int callbacksCount = mCallbacksList.beginBroadcast();
1314             if (DEBUG_LOG_ENABLED) {
1315                 Log.v(TAG, "Sending message " + msgType + " version " + msgVersion
1316                         + " from hubHandle " + contextHubHandle + ", appInstance " + appInstance
1317                         + ", callBackCount " + callbacksCount);
1318             }
1319 
1320             if (callbacksCount < 1) {
1321                 if (DEBUG_LOG_ENABLED) {
1322                     Log.v(TAG, "No message callbacks registered.");
1323                 }
1324                 return 0;
1325             }
1326 
1327             ContextHubMessage msg = new ContextHubMessage(msgType, msgVersion, data);
1328             for (int i = 0; i < callbacksCount; ++i) {
1329                 IContextHubCallback callback = mCallbacksList.getBroadcastItem(i);
1330                 try {
1331                     callback.onMessageReceipt(contextHubHandle, appInstance, msg);
1332                 } catch (RemoteException e) {
1333                     Log.i(TAG, "Exception (" + e + ") calling remote callback (" + callback + ").");
1334                     continue;
1335                 }
1336             }
1337             mCallbacksList.finishBroadcast();
1338         }
1339         return 0;
1340     }
1341 
1342     /**
1343      * Validates the HAL proxy state and context hub ID to see if we can start the transaction.
1344      *
1345      * @param contextHubId    the ID of the hub to start the transaction
1346      * @param callback        the client transaction callback interface
1347      * @param transactionType the type of the transaction
1348      * @return {@code true} if mContextHubWrapper and contextHubId is valid, {@code false} otherwise
1349      */
1350     private boolean checkHalProxyAndContextHubId(
1351             int contextHubId, IContextHubTransactionCallback callback,
1352             @ContextHubTransaction.Type int transactionType) {
1353         if (mContextHubWrapper == null) {
1354             try {
1355                 callback.onTransactionComplete(
1356                         ContextHubTransaction.RESULT_FAILED_HAL_UNAVAILABLE);
1357             } catch (RemoteException e) {
1358                 Log.e(TAG, "RemoteException while calling onTransactionComplete", e);
1359             }
1360             return false;
1361         }
1362         if (!isValidContextHubId(contextHubId)) {
1363             Log.e(TAG, "Cannot start "
1364                     + ContextHubTransaction.typeToString(transactionType, /* upperCase= */ false)
1365                     + " transaction for invalid hub ID " + contextHubId);
1366             try {
1367                 callback.onTransactionComplete(ContextHubTransaction.RESULT_FAILED_BAD_PARAMS);
1368             } catch (RemoteException e) {
1369                 Log.e(TAG, "RemoteException while calling onTransactionComplete", e);
1370             }
1371             return false;
1372         }
1373 
1374         return true;
1375     }
1376 
1377     /**
1378      * Obtains the latest location setting value and notifies the Context Hub.
1379      */
1380     private void sendLocationSettingUpdate() {
1381         boolean enabled = mContext.getSystemService(LocationManager.class)
1382                 .isLocationEnabledForUser(UserHandle.CURRENT);
1383         mContextHubWrapper.onLocationSettingChanged(enabled);
1384     }
1385 
1386     /**
1387      * Obtains the latest WiFi availability setting value and notifies the Context Hub.
1388      *
1389      * @param forceUpdate True to force send update to the Context Hub, otherwise only send the
1390      *                    update when the WiFi availability changes.
1391      */
1392     private void sendWifiSettingUpdate(boolean forceUpdate) {
1393         synchronized (mSendWifiSettingUpdateLock) {
1394             WifiManager wifiManager = mContext.getSystemService(WifiManager.class);
1395             boolean wifiEnabled = wifiManager.isWifiEnabled();
1396             boolean wifiScanEnabled = wifiManager.isScanAlwaysAvailable();
1397             boolean wifiAvailable = wifiEnabled || wifiScanEnabled;
1398             if (forceUpdate || mIsWifiAvailable != wifiAvailable) {
1399                 mIsWifiAvailable = wifiAvailable;
1400                 mContextHubWrapper.onWifiSettingChanged(wifiAvailable);
1401             }
1402             if (forceUpdate || mIsWifiScanningEnabled != wifiScanEnabled) {
1403                 mIsWifiScanningEnabled = wifiScanEnabled;
1404                 mContextHubWrapper.onWifiScanningSettingChanged(wifiScanEnabled);
1405             }
1406             if (forceUpdate || mIsWifiMainEnabled != wifiEnabled) {
1407                 mIsWifiMainEnabled = wifiEnabled;
1408                 mContextHubWrapper.onWifiMainSettingChanged(wifiEnabled);
1409             }
1410         }
1411     }
1412 
1413     /**
1414      * Obtains the latest BT availability setting value and notifies the Context Hub.
1415      *
1416      * @param forceUpdate True to force send update to the Context Hub, otherwise only send the
1417      *                    update when the BT availability changes.
1418      */
1419     private void sendBtSettingUpdate(boolean forceUpdate) {
1420         final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
1421         // Adapter may be null if BT is not supported.
1422         if (adapter != null) {
1423             boolean btEnabled = adapter.isEnabled();
1424             boolean btScanEnabled = adapter.isBleScanAlwaysAvailable();
1425             if (forceUpdate || mIsBtScanningEnabled != btScanEnabled) {
1426                 mIsBtScanningEnabled = btScanEnabled;
1427                 mContextHubWrapper.onBtScanningSettingChanged(btScanEnabled);
1428             }
1429             if (forceUpdate || mIsBtMainEnabled != btEnabled) {
1430                 mIsBtMainEnabled = btEnabled;
1431                 mContextHubWrapper.onBtMainSettingChanged(btEnabled);
1432             }
1433         } else {
1434             Log.d(TAG, "BT adapter not available. Defaulting to disabled");
1435             if (forceUpdate || mIsBtMainEnabled) {
1436                 mIsBtMainEnabled = false;
1437                 mContextHubWrapper.onBtMainSettingChanged(mIsBtMainEnabled);
1438             }
1439             if (forceUpdate || mIsBtScanningEnabled) {
1440                 mIsBtScanningEnabled = false;
1441                 mContextHubWrapper.onBtScanningSettingChanged(mIsBtScanningEnabled);
1442             }
1443         }
1444     }
1445 
1446     /**
1447      * Obtains the latest airplane mode setting value and notifies the Context Hub.
1448      */
1449     private void sendAirplaneModeSettingUpdate() {
1450         boolean enabled =
1451                 (Settings.Global.getInt(mContext.getContentResolver(),
1452                         Settings.Global.AIRPLANE_MODE_ON, 0)
1453                         == 1);
1454         mContextHubWrapper.onAirplaneModeSettingChanged(enabled);
1455     }
1456 
1457     /**
1458      * Notifies a microphone disable settings change to the Context Hub.
1459      */
1460     private void sendMicrophoneDisableSettingUpdate(boolean enabled) {
1461         Log.d(TAG, "Mic Disabled Setting: " + enabled);
1462         // The SensorPrivacyManager reports if microphone privacy was enabled,
1463         // which translates to microphone access being disabled (and vice-versa).
1464         // With this in mind, we flip the argument before piping it to CHRE.
1465         mContextHubWrapper.onMicrophoneSettingChanged(!enabled);
1466     }
1467 
1468     /**
1469      * Obtains the latest microphone disabled setting for the current user and notifies the Context
1470      * Hub.
1471      */
1472     private void sendMicrophoneDisableSettingUpdateForCurrentUser() {
1473         boolean isEnabled = mSensorPrivacyManagerInternal == null ? false :
1474                 mSensorPrivacyManagerInternal.isSensorPrivacyEnabled(
1475                 getCurrentUserId(), SensorPrivacyManager.Sensors.MICROPHONE);
1476         sendMicrophoneDisableSettingUpdate(isEnabled);
1477     }
1478 
1479     /**
1480      * Invokes a daily timer to query all context hubs
1481      */
1482     private void scheduleDailyMetricSnapshot() {
1483         Runnable queryAllContextHub = () -> {
1484             for (int contextHubId : mContextHubIdToInfoMap.keySet()) {
1485                 mMetricQueryPendingContextHubIds.add(contextHubId);
1486                 queryNanoAppsInternal(contextHubId);
1487             }
1488         };
1489         try {
1490             mDailyMetricTimer.schedule(queryAllContextHub, PERIOD_METRIC_QUERY_DAYS,
1491                     TimeUnit.DAYS);
1492         } catch (Exception e) {
1493             Log.e(TAG, "Error when schedule a timer", e);
1494         }
1495     }
1496 
1497     private String getCallingPackageName() {
1498         return mContext.getPackageManager().getNameForUid(Binder.getCallingUid());
1499     }
1500 
1501     private int getCurrentUserId() {
1502         final long id = Binder.clearCallingIdentity();
1503         try {
1504             UserInfo currentUser = ActivityManager.getService().getCurrentUser();
1505             return currentUser.id;
1506         } catch (RemoteException e) {
1507             // Activity manager not running, nothing we can do - assume user 0.
1508         } finally {
1509             Binder.restoreCallingIdentity(id);
1510         }
1511         return UserHandle.USER_SYSTEM;
1512     }
1513 
1514     /**
1515      * Send a microphone disable settings update whenever the foreground user changes. We always
1516      * send a settings update regardless of the previous state for the same user since the CHRE
1517      * framework is expected to handle repeated identical setting update.
1518      */
1519     public void onUserChanged() {
1520         Log.d(TAG, "User changed to id: " + getCurrentUserId());
1521         sendLocationSettingUpdate();
1522         sendMicrophoneDisableSettingUpdateForCurrentUser();
1523     }
1524 }
1525