1 /* 2 * Copyright (C) 2019 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.car.companiondevicesupport.service; 18 19 import static com.android.car.connecteddevice.util.SafeLog.logd; 20 import static com.android.car.connecteddevice.util.SafeLog.loge; 21 22 import android.app.Service; 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.ServiceConnection; 29 import android.os.IBinder; 30 31 import com.android.car.companiondevicesupport.api.external.ConnectedDeviceManagerBinder; 32 import com.android.car.companiondevicesupport.api.internal.association.AssociationBinder; 33 import com.android.car.companiondevicesupport.api.internal.association.IAssociatedDeviceManager; 34 import com.android.car.companiondevicesupport.feature.LocalFeature; 35 import com.android.car.companiondevicesupport.feature.howitzer.ConnectionHowitzer; 36 import com.android.car.connecteddevice.ConnectedDeviceManager; 37 import com.android.car.connecteddevice.util.EventLog; 38 39 import java.util.ArrayList; 40 import java.util.List; 41 import java.util.concurrent.Executors; 42 import java.util.concurrent.atomic.AtomicBoolean; 43 44 /** 45 * Early start service that holds a {@link ConnectedDeviceManager} reference to support companion 46 * device features. 47 */ 48 public class CompanionDeviceSupportService extends Service { 49 50 private static final String TAG = "CompanionDeviceSupportService"; 51 52 /** 53 * When a client calls {@link Context#bindService(Intent, ServiceConnection, int)} to get the 54 * {@link IAssociatedDeviceManager}, this action is required in the param {@link Intent}. 55 */ 56 public static final String ACTION_BIND_ASSOCIATION = 57 "com.android.car.companiondevicesupport.BIND_ASSOCIATION"; 58 59 /** 60 * When a client calls {@link Context#bindService(Intent, ServiceConnection, int)} to get the 61 * IConnectedDeviceManager, this action is required in the param {@link Intent}. 62 */ 63 public static final String ACTION_BIND_CONNECTED_DEVICE_MANAGER = 64 "com.android.car.companiondevicesupport.BIND_CONNECTED_DEVICE_MANAGER"; 65 66 private final BroadcastReceiver mBleBroadcastReceiver = new BroadcastReceiver() { 67 @Override 68 public void onReceive(Context context, Intent intent) { 69 if (BluetoothAdapter.ACTION_BLE_STATE_CHANGED.equals(intent.getAction())) { 70 onBluetoothStateChanged(intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1)); 71 } 72 } 73 }; 74 75 private final List<LocalFeature> mLocalFeatures = new ArrayList<>(); 76 77 private final AtomicBoolean mIsEveryFeatureInitialized = new AtomicBoolean(false); 78 79 private ConnectedDeviceManager mConnectedDeviceManager; 80 81 private ConnectedDeviceManagerBinder mConnectedDeviceManagerBinder; 82 83 private AssociationBinder mAssociationBinder; 84 85 @Override onCreate()86 public void onCreate() { 87 super.onCreate(); 88 logd(TAG, "Service created."); 89 EventLog.onServiceStarted(); 90 mConnectedDeviceManager = new ConnectedDeviceManager(this); 91 mLocalFeatures.add(new ConnectionHowitzer(this, mConnectedDeviceManager)); 92 mConnectedDeviceManagerBinder = 93 new ConnectedDeviceManagerBinder(mConnectedDeviceManager); 94 mAssociationBinder = new AssociationBinder(mConnectedDeviceManager); 95 registerReceiver(mBleBroadcastReceiver, 96 new IntentFilter(BluetoothAdapter.ACTION_BLE_STATE_CHANGED)); 97 if (BluetoothAdapter.getDefaultAdapter().isLeEnabled()) { 98 initializeFeatures(); 99 } 100 } 101 102 @Override onBind(Intent intent)103 public IBinder onBind(Intent intent) { 104 logd(TAG, "Service bound."); 105 if (intent == null || intent.getAction() == null) { 106 return null; 107 } 108 String action = intent.getAction(); 109 switch (action) { 110 case ACTION_BIND_ASSOCIATION: 111 return mAssociationBinder; 112 case ACTION_BIND_CONNECTED_DEVICE_MANAGER: 113 return mConnectedDeviceManagerBinder; 114 default: 115 loge(TAG, "Unexpected action found while binding: " + action); 116 return null; 117 } 118 } 119 120 @Override onDestroy()121 public void onDestroy() { 122 logd(TAG, "Service destroyed."); 123 unregisterReceiver(mBleBroadcastReceiver); 124 cleanup(); 125 super.onDestroy(); 126 } 127 onBluetoothStateChanged(int state)128 private void onBluetoothStateChanged(int state) { 129 logd(TAG, "onBluetoothStateChanged: " + BluetoothAdapter.nameForState(state)); 130 switch (state) { 131 case BluetoothAdapter.STATE_BLE_ON: 132 EventLog.onBleOn(); 133 initializeFeatures(); 134 break; 135 case BluetoothAdapter.STATE_OFF: 136 cleanup(); 137 break; 138 default: 139 // Ignore. 140 } 141 } 142 cleanup()143 private void cleanup() { 144 logd(TAG, "Cleaning up features."); 145 if (!mIsEveryFeatureInitialized.get()) { 146 logd(TAG, "Features are already cleaned up. No need to clean up again."); 147 return; 148 } 149 mConnectedDeviceManager.reset(); 150 mIsEveryFeatureInitialized.set(false); 151 } 152 initializeFeatures()153 private void initializeFeatures() { 154 // Room cannot be accessed on main thread. 155 Executors.defaultThreadFactory().newThread(() -> { 156 logd(TAG, "Initializing features."); 157 if (mIsEveryFeatureInitialized.get()) { 158 logd(TAG, "Features are already initialized. No need to initialize again."); 159 return; 160 } 161 mConnectedDeviceManager.start(); 162 for (LocalFeature feature : mLocalFeatures) { 163 feature.start(); 164 } 165 mIsEveryFeatureInitialized.set(true); 166 }).start(); 167 } 168 169 /** Returns the service's instance of {@link ConnectedDeviceManager}. */ getConnectedDeviceManager()170 protected ConnectedDeviceManager getConnectedDeviceManager() { 171 return mConnectedDeviceManager; 172 } 173 } 174