Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 49725383 authored by Tom Chan's avatar Tom Chan Committed by Android (Google) Code Review
Browse files

Merge "Add WSM#provideWearableConnection API" into main

parents a68db647 b44f7864
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -3153,7 +3153,9 @@ package android.app.wearable {
  public class WearableSensingManager {
    method @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideData(@NonNull android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
    method @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideDataStream(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
    method @FlaggedApi("android.app.wearable.enable_provide_wearable_connection_api") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideWearableConnection(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
    field public static final int STATUS_ACCESS_DENIED = 5; // 0x5
    field @FlaggedApi("android.app.wearable.enable_provide_wearable_connection_api") public static final int STATUS_CHANNEL_ERROR = 7; // 0x7
    field public static final int STATUS_SERVICE_UNAVAILABLE = 3; // 0x3
    field public static final int STATUS_SUCCESS = 1; // 0x1
    field public static final int STATUS_UNKNOWN = 0; // 0x0
@@ -13429,6 +13431,7 @@ package android.service.wearable {
    method @BinderThread public abstract void onDataProvided(@NonNull android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull java.util.function.Consumer<java.lang.Integer>);
    method @BinderThread public abstract void onDataStreamProvided(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.function.Consumer<java.lang.Integer>);
    method @BinderThread public abstract void onQueryServiceStatus(@NonNull java.util.Set<java.lang.Integer>, @NonNull String, @NonNull java.util.function.Consumer<android.service.ambientcontext.AmbientContextDetectionServiceStatus>);
    method @FlaggedApi("android.app.wearable.enable_provide_wearable_connection_api") @BinderThread public void onSecureWearableConnectionProvided(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.function.Consumer<java.lang.Integer>);
    method @BinderThread public abstract void onStartDetection(@NonNull android.app.ambientcontext.AmbientContextEventRequest, @NonNull String, @NonNull java.util.function.Consumer<android.service.ambientcontext.AmbientContextDetectionServiceStatus>, @NonNull java.util.function.Consumer<android.service.ambientcontext.AmbientContextDetectionResult>);
    method public abstract void onStopDetection(@NonNull String);
    field public static final String SERVICE_INTERFACE = "android.service.wearable.WearableSensingService";
+2 −0
Original line number Diff line number Diff line
@@ -27,6 +27,8 @@ import android.os.SharedMemory;
 * @hide
 */
interface IWearableSensingManager {
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)")
     void provideWearableConnection(in ParcelFileDescriptor parcelFileDescriptor, in RemoteCallback callback);
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)")
     void provideDataStream(in ParcelFileDescriptor parcelFileDescriptor, in RemoteCallback callback);
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)")
+82 −19
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.app.ambientcontext.AmbientContextEvent;
import android.companion.CompanionDeviceManager;
import android.content.Context;
import android.os.Binder;
import android.os.ParcelFileDescriptor;
@@ -36,6 +37,8 @@ import android.os.SharedMemory;
import android.service.wearable.WearableSensingService;
import android.system.OsConstants;

import java.io.InputStream;
import java.io.OutputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.Executor;
@@ -107,6 +110,14 @@ public class WearableSensingManager {
    @FlaggedApi(Flags.FLAG_ENABLE_UNSUPPORTED_OPERATION_STATUS_CODE)
    public static final int STATUS_UNSUPPORTED_OPERATION = 6;

    /**
     * The value of the status code that indicates an error occurred in the encrypted channel backed
     * by the provided connection. See {@link #provideWearableConnection(ParcelFileDescriptor,
     * Executor, Consumer)}.
     */
    @FlaggedApi(Flags.FLAG_ENABLE_PROVIDE_WEARABLE_CONNECTION_API)
    public static final int STATUS_CHANNEL_ERROR = 7;

    /** @hide */
    @IntDef(prefix = { "STATUS_" }, value = {
            STATUS_UNKNOWN,
@@ -115,7 +126,8 @@ public class WearableSensingManager {
            STATUS_SERVICE_UNAVAILABLE,
            STATUS_WEARABLE_UNAVAILABLE,
            STATUS_ACCESS_DENIED,
            STATUS_UNSUPPORTED_OPERATION
            STATUS_UNSUPPORTED_OPERATION,
            STATUS_CHANNEL_ERROR
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface StatusCode {}
@@ -131,6 +143,60 @@ public class WearableSensingManager {
        mService = service;
    }

    /**
     * Provides a remote wearable device connection to the WearableSensingService and sends the
     * resulting status to the {@code statusConsumer} after the call.
     *
     * <p>This is used by applications that will also provide an implementation of the isolated
     * WearableSensingService.
     *
     * <p>The provided {@code wearableConnection} is expected to be a connection to a remotely
     * connected wearable device. This {@code wearableConnection} will be attached to
     * CompanionDeviceManager via {@link CompanionDeviceManager#attachSystemDataTransport(int,
     * InputStream, OutputStream)}, which will create an encrypted channel using {@code
     * wearableConnection} as the raw underlying connection. The wearable device is expected to
     * attach its side of the raw connection to its CompanionDeviceManager via the same method so
     * that the two CompanionDeviceManagers on the two devices can perform attestation and set up
     * the encrypted channel. Attestation requirements are listed in
     * com.android.server.security.AttestationVerificationPeerDeviceVerifier
     *
     * <p>A proxy to the encrypted channel will be provided to the WearableSensingService, which is
     * referred to as the secureWearableConnection in WearableSensingService. Any data written to
     * secureWearableConnection will be encrypted by CompanionDeviceManager and sent over the raw
     * {@code wearableConnection} to the remote wearable device, which is expected to use its
     * CompanionDeviceManager to decrypt the data. Encrypted data arriving at the raw {@code
     * wearableConnection} will be decrypted by CompanionDeviceManager and be readable as plain text
     * from secureWearableConnection. The raw {@code wearableConnection} provided to this method
     * will not be directly available to the WearableSensingService.
     *
     * <p>If an error occurred in the encrypted channel (such as the underlying stream closed), the
     * system will send a status code of {@link STATUS_CHANNEL_ERROR} to the {@code statusConsumer}
     * and kill the WearableSensingService process.
     *
     * <p>Before providing the secureWearableConnection, the system will restart the
     * WearableSensingService process. Other method calls into WearableSensingService may be dropped
     * during the restart. The caller is responsible for ensuring other method calls are queued
     * until a success status is returned from the {@code statusConsumer}.
     *
     * @param wearableConnection The connection to provide
     * @param executor Executor on which to run the consumer callback
     * @param statusConsumer A consumer that handles the status codes for providing the connection
     *     and errors in the encrypted channel.
     */
    @RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)
    @FlaggedApi(Flags.FLAG_ENABLE_PROVIDE_WEARABLE_CONNECTION_API)
    public void provideWearableConnection(
            @NonNull ParcelFileDescriptor wearableConnection,
            @NonNull @CallbackExecutor Executor executor,
            @NonNull @StatusCode Consumer<Integer> statusConsumer) {
        try {
            RemoteCallback callback = createStatusCallback(executor, statusConsumer);
            mService.provideWearableConnection(wearableConnection, callback);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Provides a data stream to the WearableSensingService that's backed by the
     * parcelFileDescriptor, and sends the result to the {@link Consumer} right after the call.
@@ -149,15 +215,7 @@ public class WearableSensingManager {
            @NonNull @CallbackExecutor Executor executor,
            @NonNull @StatusCode Consumer<Integer> statusConsumer) {
        try {
            RemoteCallback callback = new RemoteCallback(result -> {
                int status = result.getInt(STATUS_RESPONSE_BUNDLE_KEY);
                final long identity = Binder.clearCallingIdentity();
                try {
                    executor.execute(() -> statusConsumer.accept(status));
                } finally {
                    Binder.restoreCallingIdentity(identity);
                }
            });
            RemoteCallback callback = createStatusCallback(executor, statusConsumer);
            mService.provideDataStream(parcelFileDescriptor, callback);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
@@ -191,7 +249,17 @@ public class WearableSensingManager {
            @NonNull @CallbackExecutor Executor executor,
            @NonNull @StatusCode Consumer<Integer> statusConsumer) {
        try {
            RemoteCallback callback = new RemoteCallback(result -> {
            RemoteCallback callback = createStatusCallback(executor, statusConsumer);
            mService.provideData(data, sharedMemory, callback);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    private static RemoteCallback createStatusCallback(
            Executor executor, Consumer<Integer> statusConsumer) {
        return new RemoteCallback(
                result -> {
                    int status = result.getInt(STATUS_RESPONSE_BUNDLE_KEY);
                    final long identity = Binder.clearCallingIdentity();
                    try {
@@ -200,10 +268,5 @@ public class WearableSensingManager {
                        Binder.restoreCallingIdentity(identity);
                    }
                });
            mService.provideData(data, sharedMemory, callback);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
    }
}

}
+1 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import android.os.SharedMemory;
 * @hide
 */
oneway interface IWearableSensingService {
    void provideSecureWearableConnection(in ParcelFileDescriptor parcelFileDescriptor, in RemoteCallback callback);
    void provideDataStream(in ParcelFileDescriptor parcelFileDescriptor, in RemoteCallback callback);
    void provideData(in PersistableBundle data, in SharedMemory sharedMemory, in RemoteCallback callback);
    void startDetection(in AmbientContextEventRequest request, in String packageName,
+83 −41
Original line number Diff line number Diff line
@@ -17,12 +17,14 @@
package android.service.wearable;

import android.annotation.BinderThread;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.app.Service;
import android.app.ambientcontext.AmbientContextEvent;
import android.app.ambientcontext.AmbientContextEventRequest;
import android.app.wearable.Flags;
import android.app.wearable.WearableSensingManager;
import android.content.Intent;
import android.os.Bundle;
@@ -39,6 +41,7 @@ import java.util.Arrays;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Consumer;

/**
@@ -92,19 +95,22 @@ public abstract class WearableSensingService extends Service {
    public final IBinder onBind(@NonNull Intent intent) {
        if (SERVICE_INTERFACE.equals(intent.getAction())) {
            return new IWearableSensingService.Stub() {
                /** {@inheritDoc} */
                @Override
                public void provideSecureWearableConnection(
                        ParcelFileDescriptor secureWearableConnection, RemoteCallback callback) {
                    Objects.requireNonNull(secureWearableConnection);
                    Consumer<Integer> consumer = createWearableStatusConsumer(callback);
                    WearableSensingService.this.onSecureWearableConnectionProvided(
                            secureWearableConnection, consumer);
                }

                /** {@inheritDoc} */
                @Override
                public void provideDataStream(
                        ParcelFileDescriptor parcelFileDescriptor,
                        RemoteCallback callback) {
                        ParcelFileDescriptor parcelFileDescriptor, RemoteCallback callback) {
                    Objects.requireNonNull(parcelFileDescriptor);
                    Consumer<Integer> consumer = response -> {
                        Bundle bundle = new Bundle();
                        bundle.putInt(
                                STATUS_RESPONSE_BUNDLE_KEY,
                                response);
                        callback.sendResult(bundle);
                    };
                    Consumer<Integer> consumer = createWearableStatusConsumer(callback);
                    WearableSensingService.this.onDataStreamProvided(
                            parcelFileDescriptor, consumer);
                }
@@ -116,35 +122,35 @@ public abstract class WearableSensingService extends Service {
                        SharedMemory sharedMemory,
                        RemoteCallback callback) {
                    Objects.requireNonNull(data);
                    Consumer<Integer> consumer = response -> {
                        Bundle bundle = new Bundle();
                        bundle.putInt(
                                STATUS_RESPONSE_BUNDLE_KEY,
                                response);
                        callback.sendResult(bundle);
                    };
                    Consumer<Integer> consumer = createWearableStatusConsumer(callback);
                    WearableSensingService.this.onDataProvided(data, sharedMemory, consumer);
                }

                /** {@inheritDoc} */
                @Override
                public void startDetection(@NonNull AmbientContextEventRequest request,
                        String packageName, RemoteCallback detectionResultCallback,
                public void startDetection(
                        @NonNull AmbientContextEventRequest request,
                        String packageName,
                        RemoteCallback detectionResultCallback,
                        RemoteCallback statusCallback) {
                    Objects.requireNonNull(request);
                    Objects.requireNonNull(packageName);
                    Objects.requireNonNull(detectionResultCallback);
                    Objects.requireNonNull(statusCallback);
                    Consumer<AmbientContextDetectionResult> detectionResultConsumer = result -> {
                    Consumer<AmbientContextDetectionResult> detectionResultConsumer =
                            result -> {
                                Bundle bundle = new Bundle();
                                bundle.putParcelable(
                                AmbientContextDetectionResult.RESULT_RESPONSE_BUNDLE_KEY, result);
                                        AmbientContextDetectionResult.RESULT_RESPONSE_BUNDLE_KEY,
                                        result);
                                detectionResultCallback.sendResult(bundle);
                            };
                    Consumer<AmbientContextDetectionServiceStatus> statusConsumer = status -> {
                    Consumer<AmbientContextDetectionServiceStatus> statusConsumer =
                            status -> {
                                Bundle bundle = new Bundle();
                                bundle.putParcelable(
                                AmbientContextDetectionServiceStatus.STATUS_RESPONSE_BUNDLE_KEY,
                                        AmbientContextDetectionServiceStatus
                                                .STATUS_RESPONSE_BUNDLE_KEY,
                                        status);
                                statusCallback.sendResult(bundle);
                            };
@@ -162,15 +168,19 @@ public abstract class WearableSensingService extends Service {

                /** {@inheritDoc} */
                @Override
                public void queryServiceStatus(@AmbientContextEvent.EventCode int[] eventTypes,
                        String packageName, RemoteCallback callback) {
                public void queryServiceStatus(
                        @AmbientContextEvent.EventCode int[] eventTypes,
                        String packageName,
                        RemoteCallback callback) {
                    Objects.requireNonNull(eventTypes);
                    Objects.requireNonNull(packageName);
                    Objects.requireNonNull(callback);
                    Consumer<AmbientContextDetectionServiceStatus> consumer = response -> {
                    Consumer<AmbientContextDetectionServiceStatus> consumer =
                            response -> {
                                Bundle bundle = new Bundle();
                                bundle.putParcelable(
                                AmbientContextDetectionServiceStatus.STATUS_RESPONSE_BUNDLE_KEY,
                                        AmbientContextDetectionServiceStatus
                                                .STATUS_RESPONSE_BUNDLE_KEY,
                                        response);
                                callback.sendResult(bundle);
                            };
@@ -178,13 +188,36 @@ public abstract class WearableSensingService extends Service {
                    WearableSensingService.this.onQueryServiceStatus(
                            new HashSet<>(Arrays.asList(events)), packageName, consumer);
                }

            };
        }
        Slog.w(TAG, "Incorrect service interface, returning null.");
        return null;
    }

    /**
     * Called when a secure connection to the wearable is available. See {@link
     * WearableSensingManager#provideWearableConnection(ParcelFileDescriptor, Executor, Consumer)}
     * for details about the secure connection.
     *
     * <p>When the {@code secureWearableConnection} is closed, the system will send a {@link
     * WearableSensingManager#STATUS_CHANNEL_ERROR} status code to the status consumer provided by
     * the caller of {@link WearableSensingManager#provideWearableConnection(ParcelFileDescriptor,
     * Executor, Consumer)}.
     *
     * <p>The implementing class should override this method. It should return an appropriate status
     * code via {@code statusConsumer} after receiving the {@code secureWearableConnection}.
     *
     * @param secureWearableConnection The secure connection to the wearable.
     * @param statusConsumer The consumer for the service status.
     */
    @FlaggedApi(Flags.FLAG_ENABLE_PROVIDE_WEARABLE_CONNECTION_API)
    @BinderThread
    public void onSecureWearableConnectionProvided(
            @NonNull ParcelFileDescriptor secureWearableConnection,
            @NonNull Consumer<Integer> statusConsumer) {
        statusConsumer.accept(WearableSensingManager.STATUS_UNSUPPORTED_OPERATION);
    }

    /**
     * Called when a data stream to the wearable is provided. This data stream can be used to obtain
     * data from a wearable device. It is up to the implementation to maintain the data stream and
@@ -275,4 +308,13 @@ public abstract class WearableSensingService extends Service {
        }
        return intArray;
    }

    @NonNull
    private static Consumer<Integer> createWearableStatusConsumer(RemoteCallback statusCallback) {
        return response -> {
            Bundle bundle = new Bundle();
            bundle.putInt(STATUS_RESPONSE_BUNDLE_KEY, response);
            statusCallback.sendResult(bundle);
        };
    }
}
Loading