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 18 package android.companion; 19 20 import android.annotation.MainThread; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.RequiresPermission; 24 import android.annotation.TestApi; 25 import android.app.Service; 26 import android.content.Intent; 27 import android.os.Handler; 28 import android.os.IBinder; 29 import android.util.Log; 30 31 import java.util.Objects; 32 33 /** 34 * A service that receives calls from the system when the associated companion device appears 35 * nearby or is connected, as well as when the device is no longer "present" or connected. 36 * See {@link #onDeviceAppeared(AssociationInfo)}/{@link #onDeviceDisappeared(AssociationInfo)}. 37 * 38 * <p> 39 * Companion applications must create a service that {@code extends} 40 * {@link CompanionDeviceService}, and declare it in their AndroidManifest.xml with the 41 * "android.permission.BIND_COMPANION_DEVICE_SERVICE" permission 42 * (see {@link android.Manifest.permission#BIND_COMPANION_DEVICE_SERVICE}), 43 * as well as add an intent filter for the "android.companion.CompanionDeviceService" action 44 * (see {@link #SERVICE_INTERFACE}). 45 * 46 * <p> 47 * Following is an example of such declaration: 48 * <pre>{@code 49 * <service 50 * android:name=".CompanionService" 51 * android:label="@string/service_name" 52 * android:exported="true" 53 * android:permission="android.permission.BIND_COMPANION_DEVICE_SERVICE"> 54 * <intent-filter> 55 * <action android:name="android.companion.CompanionDeviceService" /> 56 * </intent-filter> 57 * </service> 58 * }</pre> 59 * 60 * <p> 61 * If the companion application has requested observing device presence (see 62 * {@link CompanionDeviceManager#startObservingDevicePresence(String)}) the system will 63 * <a href="https://developer.android.com/guide/components/bound-services"> bind the service</a> 64 * when it detects the device nearby (for BLE devices) or when the device is connected 65 * (for Bluetooth devices). 66 * 67 * <p> 68 * The system binding {@link CompanionDeviceService} elevates the priority of the process that 69 * the service is running in, and thus may prevent 70 * <a href="https://developer.android.com/topic/performance/memory-management#low-memory_killer"> 71 * the Low-memory killer</a> from killing the process at expense of other processes with lower 72 * priority. 73 * 74 * <p> 75 * It is possible for an application to declare multiple {@link CompanionDeviceService}-s. 76 * In such case, the system will bind all declared services, but will deliver 77 * {@link #onDeviceAppeared(AssociationInfo)} and {@link #onDeviceDisappeared(AssociationInfo)} 78 * only to one "primary" services. 79 * Applications that declare multiple {@link CompanionDeviceService}-s should indicate the "primary" 80 * service using "android.companion.PROPERTY_PRIMARY_COMPANION_DEVICE_SERVICE" service level 81 * property. 82 * <pre>{@code 83 * <property 84 * android:name="android.companion.PROPERTY_PRIMARY_COMPANION_DEVICE_SERVICE" 85 * android:value="true" /> 86 * }</pre> 87 * 88 * <p> 89 * If the application declares multiple {@link CompanionDeviceService}-s, but does not indicate 90 * the "primary" one, the system will pick one of the declared services to use as "primary". 91 * 92 * <p> 93 * If the application declares multiple "primary" {@link CompanionDeviceService}-s, the system 94 * will pick single one of them to use as "primary". 95 */ 96 public abstract class CompanionDeviceService extends Service { 97 98 private static final String LOG_TAG = "CompanionDeviceService"; 99 100 /** 101 * An intent action for a service to be bound whenever this app's companion device(s) 102 * are nearby. 103 * 104 * <p>The app will be kept alive for as long as the device is nearby or companion app reports 105 * appeared. 106 * If the app is not running at the time device gets connected, the app will be woken up.</p> 107 * 108 * <p>Shortly after the device goes out of range or the companion app reports disappeared, 109 * the service will be unbound, and the app will be eligible for cleanup, unless any other 110 * user-visible components are running.</p> 111 * 112 * If running in background is not essential for the devices that this app can manage, 113 * app should avoid declaring this service.</p> 114 * 115 * <p>The service must also require permission 116 * {@link android.Manifest.permission#BIND_COMPANION_DEVICE_SERVICE}</p> 117 */ 118 public static final String SERVICE_INTERFACE = "android.companion.CompanionDeviceService"; 119 120 private final Stub mRemote = new Stub(); 121 122 /** 123 * Called by system whenever a device associated with this app is available. 124 * 125 * @param address the MAC address of the device 126 * @deprecated please override {@link #onDeviceAppeared(AssociationInfo)} instead. 127 */ 128 @Deprecated 129 @MainThread onDeviceAppeared(@onNull String address)130 public void onDeviceAppeared(@NonNull String address) { 131 // Do nothing. Companion apps can override this function. 132 } 133 134 /** 135 * Called by system whenever a device associated with this app stops being available. 136 * 137 * Usually this means the device goes out of range or is turned off. 138 * 139 * @param address the MAC address of the device 140 * @deprecated please override {@link #onDeviceDisappeared(AssociationInfo)} instead. 141 */ 142 @Deprecated 143 @MainThread onDeviceDisappeared(@onNull String address)144 public void onDeviceDisappeared(@NonNull String address) { 145 // Do nothing. Companion apps can override this function. 146 } 147 148 /** 149 * Called by system whenever the system dispatches a message to the app to send it to 150 * an associated device. 151 * 152 * @param messageId system assigned id of the message to be sent 153 * @param associationId association id of the associated device 154 * @param message message to be sent 155 * 156 * @hide 157 */ 158 @MainThread onDispatchMessage(int messageId, int associationId, @NonNull byte[] message)159 public void onDispatchMessage(int messageId, int associationId, @NonNull byte[] message) { 160 // do nothing. Companion apps can override this function for system to send messages. 161 } 162 163 /** 164 * App calls this method when there's a message received from an associated device, 165 * which needs to be dispatched to system for processing. 166 * 167 * <p>Calling app must declare uses-permission 168 * {@link android.Manifest.permission#DELIVER_COMPANION_MESSAGES}</p> 169 * 170 * @param messageId id of the message 171 * @param associationId id of the associated device 172 * @param message messaged received from the associated device 173 * 174 * @hide 175 */ 176 @RequiresPermission(android.Manifest.permission.DELIVER_COMPANION_MESSAGES) dispatchMessage(int messageId, int associationId, @NonNull byte[] message)177 public final void dispatchMessage(int messageId, int associationId, @NonNull byte[] message) { 178 CompanionDeviceManager companionDeviceManager = 179 getSystemService(CompanionDeviceManager.class); 180 companionDeviceManager.dispatchMessage(messageId, associationId, message); 181 } 182 183 /** 184 * Called by system whenever a device associated with this app is connected. 185 * 186 * @param associationInfo A record for the companion device. 187 */ 188 @MainThread onDeviceAppeared(@onNull AssociationInfo associationInfo)189 public void onDeviceAppeared(@NonNull AssociationInfo associationInfo) { 190 if (!associationInfo.isSelfManaged()) { 191 onDeviceAppeared(associationInfo.getDeviceMacAddressAsString()); 192 } 193 } 194 195 /** 196 * Called by system whenever a device associated with this app is disconnected. 197 * 198 * @param associationInfo A record for the companion device. 199 */ 200 @MainThread onDeviceDisappeared(@onNull AssociationInfo associationInfo)201 public void onDeviceDisappeared(@NonNull AssociationInfo associationInfo) { 202 if (!associationInfo.isSelfManaged()) { 203 onDeviceDisappeared(associationInfo.getDeviceMacAddressAsString()); 204 } 205 } 206 207 @Nullable 208 @Override onBind(@onNull Intent intent)209 public final IBinder onBind(@NonNull Intent intent) { 210 if (Objects.equals(intent.getAction(), SERVICE_INTERFACE)) { 211 onBindCompanionDeviceService(intent); 212 return mRemote; 213 } 214 Log.w(LOG_TAG, 215 "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + "): " + intent); 216 return null; 217 } 218 219 /** 220 * Used to track the state of Binder connection in CTS tests. 221 * @hide 222 */ 223 @TestApi onBindCompanionDeviceService(@onNull Intent intent)224 public void onBindCompanionDeviceService(@NonNull Intent intent) { 225 } 226 227 private class Stub extends ICompanionDeviceService.Stub { 228 final Handler mMainHandler = Handler.getMain(); 229 final CompanionDeviceService mService = CompanionDeviceService.this; 230 231 @Override onDeviceAppeared(AssociationInfo associationInfo)232 public void onDeviceAppeared(AssociationInfo associationInfo) { 233 mMainHandler.postAtFrontOfQueue(() -> mService.onDeviceAppeared(associationInfo)); 234 } 235 236 @Override onDeviceDisappeared(AssociationInfo associationInfo)237 public void onDeviceDisappeared(AssociationInfo associationInfo) { 238 mMainHandler.postAtFrontOfQueue(() -> mService.onDeviceDisappeared(associationInfo)); 239 } 240 241 @Override onDispatchMessage(int messageId, int associationId, @NonNull byte[] message)242 public void onDispatchMessage(int messageId, int associationId, @NonNull byte[] message) { 243 mMainHandler.postAtFrontOfQueue( 244 () -> mService.onDispatchMessage(messageId, associationId, message)); 245 } 246 } 247 } 248