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