diff --git a/api/current.txt b/api/current.txt index 93eb32caf6976..2d654f0da4e15 100644 --- a/api/current.txt +++ b/api/current.txt @@ -31,6 +31,7 @@ package android { field @Deprecated public static final String BIND_CARRIER_MESSAGING_SERVICE = "android.permission.BIND_CARRIER_MESSAGING_SERVICE"; field public static final String BIND_CARRIER_SERVICES = "android.permission.BIND_CARRIER_SERVICES"; field @Deprecated public static final String BIND_CHOOSER_TARGET_SERVICE = "android.permission.BIND_CHOOSER_TARGET_SERVICE"; + field public static final String BIND_COMPANION_DEVICE_SERVICE = "android.permission.BIND_COMPANION_DEVICE_SERVICE"; field public static final String BIND_CONDITION_PROVIDER_SERVICE = "android.permission.BIND_CONDITION_PROVIDER_SERVICE"; field public static final String BIND_CONTROLS = "android.permission.BIND_CONTROLS"; field public static final String BIND_DEVICE_ADMIN = "android.permission.BIND_DEVICE_ADMIN"; @@ -9491,6 +9492,14 @@ package android.companion { method public abstract void onFailure(CharSequence); } + public abstract class CompanionDeviceService extends android.app.Service { + ctor public CompanionDeviceService(); + method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent); + method @MainThread public abstract void onDeviceAppeared(@NonNull String); + method @MainThread public abstract void onDeviceDisappeared(@NonNull String); + field public static final String SERVICE_INTERFACE = "android.companion.CompanionDeviceService"; + } + public interface DeviceFilter extends android.os.Parcelable { } diff --git a/core/api/current.txt b/core/api/current.txt index 9329e2ae47fc4..2b22af4fb9358 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -31,6 +31,7 @@ package android { field @Deprecated public static final String BIND_CARRIER_MESSAGING_SERVICE = "android.permission.BIND_CARRIER_MESSAGING_SERVICE"; field public static final String BIND_CARRIER_SERVICES = "android.permission.BIND_CARRIER_SERVICES"; field @Deprecated public static final String BIND_CHOOSER_TARGET_SERVICE = "android.permission.BIND_CHOOSER_TARGET_SERVICE"; + field public static final String BIND_COMPANION_DEVICE_SERVICE = "android.permission.BIND_COMPANION_DEVICE_SERVICE"; field public static final String BIND_CONDITION_PROVIDER_SERVICE = "android.permission.BIND_CONDITION_PROVIDER_SERVICE"; field public static final String BIND_CONTROLS = "android.permission.BIND_CONTROLS"; field public static final String BIND_DEVICE_ADMIN = "android.permission.BIND_DEVICE_ADMIN"; @@ -9491,6 +9492,14 @@ package android.companion { method public abstract void onFailure(CharSequence); } + public abstract class CompanionDeviceService extends android.app.Service { + ctor public CompanionDeviceService(); + method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent); + method @MainThread public abstract void onDeviceAppeared(@NonNull String); + method @MainThread public abstract void onDeviceDisappeared(@NonNull String); + field public static final String SERVICE_INTERFACE = "android.companion.CompanionDeviceService"; + } + public interface DeviceFilter extends android.os.Parcelable { } diff --git a/core/java/android/companion/CompanionDeviceService.java b/core/java/android/companion/CompanionDeviceService.java new file mode 100644 index 0000000000000..56639ea36bf6a --- /dev/null +++ b/core/java/android/companion/CompanionDeviceService.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package android.companion; + +import android.annotation.MainThread; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.Service; +import android.content.Intent; +import android.os.Handler; +import android.os.IBinder; +import android.util.Log; + +import com.android.internal.util.function.pooled.PooledLambda; + +import java.util.Objects; + +/** + * Service to be implemented by apps that manage a companion device. + * + * System will keep this service bound whenever an associated device is nearby, + * ensuring app stays alive. + * + * An app must be {@link CompanionDeviceManager#associate associated} with at leas one device, + * before it can take advantage of this service. + * + * You must declare this service in your manifest with an + * intent-filter action of {@link #SERVICE_INTERFACE} and + * permission of {@link android.Manifest.permission#BIND_COMPANION_DEVICE_SERVICE} + */ +public abstract class CompanionDeviceService extends Service { + + private static final String LOG_TAG = "CompanionDeviceService"; + + /** + * An intent action for a service to be bound whenever this app's companion device(s) + * are nearby. + * + *

The app will be kept alive for as long as the device is nearby. + * If the app is not running at the time device gets connected, the app will be woken up.

+ * + *

Shortly after the device goes out of range, the service will be unbound, and the + * app will be eligible for cleanup, unless any other user-visible components are running.

+ * + *

An app shouldn't declare more than one of these services. + * If running in background is not essential for the devices that this app can manage, + * app should avoid declaring this service.

+ * + *

The service must also require permission + * {@link android.Manifest.permission#BIND_COMPANION_DEVICE_SERVICE}

+ */ + public static final String SERVICE_INTERFACE = "android.companion.CompanionDeviceService"; + + private final Stub mRemote = new Stub(); + + /** + * Called by system whenever a device associated with this app is available. + * + * @param address the MAC address of the device + */ + @MainThread + public abstract void onDeviceAppeared(@NonNull String address); + + /** + * Called by system whenever a device associated with this app stops being available. + * + * Usually this means the device goes out of range or is turned off. + * + * @param address the MAC address of the device + */ + @MainThread + public abstract void onDeviceDisappeared(@NonNull String address); + + @Nullable + @Override + public final IBinder onBind(@NonNull Intent intent) { + if (Objects.equals(intent.getAction(), SERVICE_INTERFACE)) { + return mRemote; + } + Log.w(LOG_TAG, + "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + "): " + intent); + return null; + } + + class Stub extends ICompanionDeviceService.Stub { + + @Override + public void onDeviceAppeared(String address) { + Handler.getMain().sendMessage(PooledLambda.obtainMessage( + CompanionDeviceService::onDeviceAppeared, + CompanionDeviceService.this, address)); + } + + @Override + public void onDeviceDisappeared(String address) { + Handler.getMain().sendMessage(PooledLambda.obtainMessage( + CompanionDeviceService::onDeviceDisappeared, + CompanionDeviceService.this, address)); + } + } +} diff --git a/core/java/android/companion/ICompanionDeviceService.aidl b/core/java/android/companion/ICompanionDeviceService.aidl new file mode 100644 index 0000000000000..f3977ca4af6fe --- /dev/null +++ b/core/java/android/companion/ICompanionDeviceService.aidl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.companion; + +import android.app.PendingIntent; +import android.companion.IFindDeviceCallback; +import android.companion.Association; +import android.companion.AssociationRequest; +import android.content.ComponentName; + +/** @hide */ +oneway interface ICompanionDeviceService { + void onDeviceAppeared(in String address); + void onDeviceDisappeared(in String address); +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index a778f14af7659..8ec965df98dcb 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3400,6 +3400,12 @@ + + +