diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 595e38aa55767..a003cf0581f92 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -79,6 +79,7 @@ package android { field public static final String BIND_TRUST_AGENT = "android.permission.BIND_TRUST_AGENT"; field public static final String BIND_TV_REMOTE_SERVICE = "android.permission.BIND_TV_REMOTE_SERVICE"; field public static final String BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE = "android.permission.BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE"; + field public static final String BIND_WEARABLE_SENSING_SERVICE = "android.permission.BIND_WEARABLE_SENSING_SERVICE"; field public static final String BLUETOOTH_MAP = "android.permission.BLUETOOTH_MAP"; field public static final String BRICK = "android.permission.BRICK"; field public static final String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE"; @@ -199,6 +200,7 @@ package android { field public static final String MANAGE_USER_OEM_UNLOCK_STATE = "android.permission.MANAGE_USER_OEM_UNLOCK_STATE"; field public static final String MANAGE_WALLPAPER_EFFECTS_GENERATION = "android.permission.MANAGE_WALLPAPER_EFFECTS_GENERATION"; field public static final String MANAGE_WEAK_ESCROW_TOKEN = "android.permission.MANAGE_WEAK_ESCROW_TOKEN"; + field public static final String MANAGE_WEARABLE_SENSING_SERVICE = "android.permission.MANAGE_WEARABLE_SENSING_SERVICE"; field public static final String MANAGE_WIFI_COUNTRY_CODE = "android.permission.MANAGE_WIFI_COUNTRY_CODE"; field public static final String MARK_DEVICE_ORGANIZATION_OWNED = "android.permission.MARK_DEVICE_ORGANIZATION_OWNED"; field public static final String MEDIA_RESOURCE_OVERRIDE_PID = "android.permission.MEDIA_RESOURCE_OVERRIDE_PID"; @@ -2825,6 +2827,21 @@ package android.app.wallpapereffectsgeneration { } +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); + 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); + field public static final int STATUS_ACCESS_DENIED = 5; // 0x5 + 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 + field public static final int STATUS_UNSUPPORTED = 2; // 0x2 + field public static final int STATUS_WEARABLE_UNAVAILABLE = 4; // 0x4 + } + +} + package android.apphibernation { public class AppHibernationManager { @@ -3074,6 +3091,7 @@ package android.content { field public static final String UWB_SERVICE = "uwb"; field public static final String VR_SERVICE = "vrmanager"; field public static final String WALLPAPER_EFFECTS_GENERATION_SERVICE = "wallpaper_effects_generation"; + field public static final String WEARABLE_SENSING_SERVICE = "wearable_sensing"; field public static final String WIFI_NL80211_SERVICE = "wifinl80211"; field @Deprecated public static final String WIFI_RTT_SERVICE = "rttmanager"; field public static final String WIFI_SCANNING_SERVICE = "wifiscanner"; @@ -10513,6 +10531,7 @@ package android.provider { field public static final String NAMESPACE_TEXTCLASSIFIER = "textclassifier"; field public static final String NAMESPACE_TRANSPARENCY_METADATA = "transparency_metadata"; field public static final String NAMESPACE_UWB = "uwb"; + field public static final String NAMESPACE_WEARABLE_SENSING = "wearable_sensing"; field public static final String NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT = "window_manager_native_boot"; } @@ -12263,6 +12282,18 @@ package android.service.watchdog { } +package android.service.wearable { + + public abstract class WearableSensingService extends android.app.Service { + ctor public WearableSensingService(); + method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent); + method @BinderThread public abstract void onDataProvided(@NonNull android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull java.util.function.Consumer); + method @BinderThread public abstract void onDataStreamProvided(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.function.Consumer); + field public static final String SERVICE_INTERFACE = "android.service.wearable.WearableSensingService"; + } + +} + package android.telecom { @Deprecated public class AudioState implements android.os.Parcelable { diff --git a/core/java/android/app/wearable/IWearableSensingManager.aidl b/core/java/android/app/wearable/IWearableSensingManager.aidl new file mode 100644 index 0000000000000..ff37bd848d610 --- /dev/null +++ b/core/java/android/app/wearable/IWearableSensingManager.aidl @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2022 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.app.wearable; + +import android.os.ParcelFileDescriptor; +import android.os.PersistableBundle; +import android.os.RemoteCallback; +import android.os.SharedMemory; + +/** + * Interface for a WearableSensingManager for managing wearable sensing service. + * + * @hide + */ +interface IWearableSensingManager { + @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)") + void provideData(in PersistableBundle data, in SharedMemory sharedMemory, in RemoteCallback callback); +} \ No newline at end of file diff --git a/core/java/android/app/wearable/OWNERS b/core/java/android/app/wearable/OWNERS new file mode 100644 index 0000000000000..073e2d79850b2 --- /dev/null +++ b/core/java/android/app/wearable/OWNERS @@ -0,0 +1,3 @@ +charliewang@google.com +oni@google.com +volnov@google.com \ No newline at end of file diff --git a/core/java/android/app/wearable/WearableSensingManager.java b/core/java/android/app/wearable/WearableSensingManager.java new file mode 100644 index 0000000000000..a71590bd1d826 --- /dev/null +++ b/core/java/android/app/wearable/WearableSensingManager.java @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2022 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.app.wearable; + +import android.Manifest; +import android.annotation.CallbackExecutor; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; +import android.annotation.SystemService; +import android.app.ambientcontext.AmbientContextEvent; +import android.content.Context; +import android.os.Binder; +import android.os.ParcelFileDescriptor; +import android.os.PersistableBundle; +import android.os.RemoteCallback; +import android.os.RemoteException; +import android.os.SharedMemory; +import android.service.wearable.WearableSensingService; +import android.system.OsConstants; + +import java.util.concurrent.Executor; +import java.util.function.Consumer; + +/** + * Allows granted apps to manage the WearableSensingService. + * Applications are responsible for managing the connection to Wearables. Applications can choose + * to provide a data stream to the WearableSensingService to use for + * computing {@link AmbientContextEvent}s. Applications can also optionally provide their own + * defined data to power the detection of {@link AmbientContextEvent}s. + * Methods on this class requires the caller to hold and be granted the + * {@link Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE}. + * + * @hide + */ + +@SystemApi +@SystemService(Context.WEARABLE_SENSING_SERVICE) +public class WearableSensingManager { + /** + * The bundle key for the service status query result, used in + * {@code RemoteCallback#sendResult}. + * + * @hide + */ + public static final String STATUS_RESPONSE_BUNDLE_KEY = + "android.app.wearable.WearableSensingStatusBundleKey"; + + + /** + * An unknown status. + */ + public static final int STATUS_UNKNOWN = 0; + + /** + * The value of the status code that indicates success. + */ + public static final int STATUS_SUCCESS = 1; + + /** + * The value of the status code that indicates one or more of the + * requested events are not supported. + */ + public static final int STATUS_UNSUPPORTED = 2; + + /** + * The value of the status code that indicates service not available. + */ + public static final int STATUS_SERVICE_UNAVAILABLE = 3; + + /** + * The value of the status code that there's no connection to the wearable. + */ + public static final int STATUS_WEARABLE_UNAVAILABLE = 4; + + /** + * The value of the status code that the app is not granted access. + */ + public static final int STATUS_ACCESS_DENIED = 5; + + /** @hide */ + @IntDef(prefix = { "STATUS_" }, value = { + STATUS_UNKNOWN, + STATUS_SUCCESS, + STATUS_UNSUPPORTED, + STATUS_SERVICE_UNAVAILABLE, + STATUS_WEARABLE_UNAVAILABLE, + STATUS_ACCESS_DENIED + }) public @interface StatusCode {} + + private final Context mContext; + private final IWearableSensingManager mService; + + /** + * {@hide} + */ + public WearableSensingManager(Context context, IWearableSensingManager service) { + mContext = context; + mService = service; + } + + /** + * 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. + * This is used by applications that will also provide an implementation of + * an isolated WearableSensingService. If the data stream was provided successfully + * {@link WearableSensingManager#STATUS_SUCCESS} will be provided. + * + * @param parcelFileDescriptor The data stream to provide + * @param executor Executor on which to run the consumer callback + * @param statusConsumer A consumer that handles the status codes, which is returned + * right after the call. + */ + @RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) + public void provideDataStream( + @NonNull ParcelFileDescriptor parcelFileDescriptor, + @NonNull @CallbackExecutor Executor executor, + @NonNull @StatusCode Consumer 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); + } + }); + mService.provideDataStream(parcelFileDescriptor, callback); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Sets configuration and provides read-only data in a {@link PersistableBundle} that may be + * used by the WearableSensingService, and sends the result to the {@link Consumer} + * right after the call. It is dependent on the application to + * define the type of data to provide. This is used by applications that will also + * provide an implementation of an isolated WearableSensingService. If the data was + * provided successfully {@link WearableSensingManager#STATUS_SUCCESS} will be povided. + * + * @param data Application configuration data to provide to the {@link WearableSensingService}. + * PersistableBundle does not allow any remotable objects or other contents + * that can be used to communicate with other processes. + * @param sharedMemory The unrestricted data blob to + * provide to the {@link WearableSensingService}. Use this to provide the + * sensing models data or other such data to the trusted process. + * The sharedMemory must be read only and protected with + * {@link OsConstants.PROT_READ}. + * Other operations will be removed by the system. + * @param executor Executor on which to run the consumer callback + * @param statusConsumer A consumer that handles the status codes, which is returned + * right after the call + */ + @RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) + public void provideData( + @NonNull PersistableBundle data, @Nullable SharedMemory sharedMemory, + @NonNull @CallbackExecutor Executor executor, + @NonNull @StatusCode Consumer 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); + } + }); + mService.provideData(data, sharedMemory, callback); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + +} diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 1df0fa8084a5f..9fc9b8f7d07c6 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -6072,6 +6072,17 @@ public abstract class Context { @SystemApi public static final String AMBIENT_CONTEXT_SERVICE = "ambient_context"; + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.app.wearable.WearableSensingManager}. + * + * @see #getSystemService(String) + * @see WearableSensingManager + * @hide + */ + @SystemApi + public static final String WEARABLE_SENSING_SERVICE = "wearable_sensing"; + /** * Use with {@link #getSystemService(String)} to retrieve a * {@link android.healthconnect.HealthConnectManager}. diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index 7095d1b20d840..c81b3404e69e0 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -764,6 +764,15 @@ public final class DeviceConfig { public static final String NAMESPACE_AMBIENT_CONTEXT_MANAGER_SERVICE = "ambient_context_manager_service"; + /** + * Namespace for WearableSensingManagerService related features. + * + * @hide + */ + @SystemApi + public static final String NAMESPACE_WEARABLE_SENSING = + "wearable_sensing"; + /** * Namespace for Vendor System Native related features. * diff --git a/core/java/android/service/wearable/IWearableSensingService.aidl b/core/java/android/service/wearable/IWearableSensingService.aidl new file mode 100644 index 0000000000000..ba7117440e9ab --- /dev/null +++ b/core/java/android/service/wearable/IWearableSensingService.aidl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2022 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.service.wearable; + +import android.os.PersistableBundle; +import android.os.RemoteCallback; +import android.os.SharedMemory; + +/** + * Interface for a concrete implementation to provide AmbientContextEvents to the framework for + * wearables. + * + * @hide + */ +oneway interface IWearableSensingService { + void provideDataStream(in ParcelFileDescriptor parcelFileDescriptor, in RemoteCallback callback); + void provideData(in PersistableBundle data, in SharedMemory sharedMemory, in RemoteCallback callback); +} \ No newline at end of file diff --git a/core/java/android/service/wearable/OWNERS b/core/java/android/service/wearable/OWNERS new file mode 100644 index 0000000000000..073e2d79850b2 --- /dev/null +++ b/core/java/android/service/wearable/OWNERS @@ -0,0 +1,3 @@ +charliewang@google.com +oni@google.com +volnov@google.com \ No newline at end of file diff --git a/core/java/android/service/wearable/WearableSensingService.java b/core/java/android/service/wearable/WearableSensingService.java new file mode 100644 index 0000000000000..a1c7658fbf65c --- /dev/null +++ b/core/java/android/service/wearable/WearableSensingService.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2022 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.service.wearable; + +import android.annotation.BinderThread; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.app.Service; +import android.app.ambientcontext.AmbientContextEvent; +import android.app.wearable.WearableSensingManager; +import android.content.Intent; +import android.os.Bundle; +import android.os.IBinder; +import android.os.ParcelFileDescriptor; +import android.os.PersistableBundle; +import android.os.RemoteCallback; +import android.os.SharedMemory; +import android.util.Slog; + +import java.util.Objects; +import java.util.function.Consumer; + +/** + * Abstract base class for sensing with wearable devices. An example of this is {@link + *AmbientContextEvent} detection. + * + *

A service that provides requested sensing events to the system, such as a {@link + *AmbientContextEvent}. The system's default WearableSensingService implementation is configured in + * {@code config_defaultWearableSensingService}. If this config has no value, a stub is + * returned. + * + *

An implementation of a WearableSensingService should be an isolated service. Using the + * "isolatedProcess=true" attribute in the service's configurations.

+ ** + *
+ * {@literal
+ * 
+ * }
+ * 
+ * + * @hide + */ +@SystemApi +public abstract class WearableSensingService extends Service { + private static final String TAG = WearableSensingService.class.getSimpleName(); + + /** + * The bundle key for this class of object, used in {@code RemoteCallback#sendResult}. + * + * @hide + */ + public static final String STATUS_RESPONSE_BUNDLE_KEY = + "android.app.wearable.WearableSensingStatusBundleKey"; + + /** + * The {@link Intent} that must be declared as handled by the service. To be supported, the + * service must also require the + * {@link android.Manifest.permission#BIND_WEARABLE_SENSING_SERVICE} + * permission so that other applications can not abuse it. + */ + public static final String SERVICE_INTERFACE = + "android.service.wearable.WearableSensingService"; + + @Nullable + @Override + public final IBinder onBind(@NonNull Intent intent) { + if (SERVICE_INTERFACE.equals(intent.getAction())) { + return new IWearableSensingService.Stub() { + /** {@inheritDoc} */ + @Override + public void provideDataStream( + ParcelFileDescriptor parcelFileDescriptor, + RemoteCallback callback) { + Objects.requireNonNull(parcelFileDescriptor); + Consumer consumer = response -> { + Bundle bundle = new Bundle(); + bundle.putInt( + STATUS_RESPONSE_BUNDLE_KEY, + response); + callback.sendResult(bundle); + }; + WearableSensingService.this.onDataStreamProvided( + parcelFileDescriptor, consumer); + } + + /** {@inheritDoc} */ + @Override + public void provideData( + PersistableBundle data, + SharedMemory sharedMemory, + RemoteCallback callback) { + Objects.requireNonNull(data); + Consumer consumer = response -> { + Bundle bundle = new Bundle(); + bundle.putInt( + STATUS_RESPONSE_BUNDLE_KEY, + response); + callback.sendResult(bundle); + }; + WearableSensingService.this.onDataProvided(data, sharedMemory, consumer); + } + }; + } + Slog.w(TAG, "Incorrect service interface, returning null."); + return null; + } + + /** + * 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 + * close the data stream when it is finished. + * + * @param parcelFileDescriptor The data stream to the wearable + * @param statusConsumer the consumer for the service status. + */ + @BinderThread + public abstract void onDataStreamProvided(@NonNull ParcelFileDescriptor parcelFileDescriptor, + @NonNull Consumer statusConsumer); + + /** + * Called when configurations and read-only data in a {@link PersistableBundle} + * can be used by the WearableSensingService and sends the result to the {@link Consumer} + * right after the call. It is dependent on the application to define the type of data to + * provide. This is used by applications that will also provide an implementation of an isolated + * WearableSensingService. If the data was provided successfully + * {@link WearableSensingManager#STATUS_SUCCESS} will be provided. + * + * @param data Application configuration data to provide to the {@link WearableSensingService}. + * PersistableBundle does not allow any remotable objects or other contents + * that can be used to communicate with other processes. + * @param sharedMemory The unrestricted data blob to + * provide to the {@link WearableSensingService}. Use this to provide the + * sensing models data or other such data to the trusted process. + * @param statusConsumer the consumer for the service status. + */ + @BinderThread + public abstract void onDataProvided( + @NonNull PersistableBundle data, + @Nullable SharedMemory sharedMemory, + @NonNull Consumer statusConsumer); +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 87f47a4f33f82..11adde5048067 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -6641,6 +6641,21 @@ + + + + + + + + + diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 7b6284196bfd5..9ca49bd953a14 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3768,6 +3768,7 @@ + diff --git a/services/core/java/com/android/server/pm/KnownPackages.java b/services/core/java/com/android/server/pm/KnownPackages.java index dcf715265138c..154709a620954 100644 --- a/services/core/java/com/android/server/pm/KnownPackages.java +++ b/services/core/java/com/android/server/pm/KnownPackages.java @@ -47,6 +47,7 @@ public final class KnownPackages { PACKAGE_RETAIL_DEMO, PACKAGE_RECENTS, PACKAGE_AMBIENT_CONTEXT_DETECTION, + PACKAGE_WEARABLE_SENSING, }) @Retention(RetentionPolicy.SOURCE) public @interface KnownPackage { @@ -71,9 +72,10 @@ public final class KnownPackages { public static final int PACKAGE_RETAIL_DEMO = 16; public static final int PACKAGE_RECENTS = 17; public static final int PACKAGE_AMBIENT_CONTEXT_DETECTION = 18; + public static final int PACKAGE_WEARABLE_SENSING = 19; // Integer value of the last known package ID. Increases as new ID is added to KnownPackage. // Please note the numbers should be continuous. - public static final int LAST_KNOWN_PACKAGE = PACKAGE_AMBIENT_CONTEXT_DETECTION; + public static final int LAST_KNOWN_PACKAGE = PACKAGE_WEARABLE_SENSING; private final DefaultAppProvider mDefaultAppProvider; private final String mRequiredInstallerPackage; @@ -86,6 +88,7 @@ public final class KnownPackages { private final String mConfiguratorPackage; private final String mIncidentReportApproverPackage; private final String mAmbientContextDetectionPackage; + private final String mWearableSensingPackage; private final String mAppPredictionServicePackage; private final String mCompanionPackage; private final String mRetailDemoPackage; @@ -97,9 +100,9 @@ public final class KnownPackages { String[] requiredVerifierPackages, String defaultTextClassifierPackage, String systemTextClassifierPackageName, String requiredPermissionControllerPackage, String configuratorPackage, String incidentReportApproverPackage, - String ambientContextDetectionPackage, String appPredictionServicePackage, - String companionPackageName, String retailDemoPackage, - String overlayConfigSignaturePackage, String recentsPackage) { + String ambientContextDetectionPackage, String wearableSensingPackage, + String appPredictionServicePackage, String companionPackageName, + String retailDemoPackage, String overlayConfigSignaturePackage, String recentsPackage) { mDefaultAppProvider = defaultAppProvider; mRequiredInstallerPackage = requiredInstallerPackage; mRequiredUninstallerPackage = requiredUninstallerPackage; @@ -111,6 +114,7 @@ public final class KnownPackages { mConfiguratorPackage = configuratorPackage; mIncidentReportApproverPackage = incidentReportApproverPackage; mAmbientContextDetectionPackage = ambientContextDetectionPackage; + mWearableSensingPackage = wearableSensingPackage; mAppPredictionServicePackage = appPredictionServicePackage; mCompanionPackage = companionPackageName; mRetailDemoPackage = retailDemoPackage; @@ -165,6 +169,8 @@ public final class KnownPackages { return "Recents"; case PACKAGE_AMBIENT_CONTEXT_DETECTION: return "Ambient Context Detection"; + case PACKAGE_WEARABLE_SENSING: + return "Wearable sensing"; } return "Unknown"; } @@ -194,6 +200,8 @@ public final class KnownPackages { return snapshot.filterOnlySystemPackages(mIncidentReportApproverPackage); case PACKAGE_AMBIENT_CONTEXT_DETECTION: return snapshot.filterOnlySystemPackages(mAmbientContextDetectionPackage); + case PACKAGE_WEARABLE_SENSING: + return snapshot.filterOnlySystemPackages(mWearableSensingPackage); case PACKAGE_APP_PREDICTOR: return snapshot.filterOnlySystemPackages(mAppPredictionServicePackage); case PACKAGE_COMPANION: diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 8089af381a950..6d0d81298f5ba 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -937,6 +937,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService final @Nullable String mOverlayConfigSignaturePackage; final @Nullable String mRecentsPackage; final @Nullable String mAmbientContextDetectionPackage; + final @Nullable String mWearableSensingPackage; private final @NonNull String mRequiredSdkSandboxPackage; @GuardedBy("mLock") @@ -1682,6 +1683,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService mRetailDemoPackage = testParams.retailDemoPackage; mRecentsPackage = testParams.recentsPackage; mAmbientContextDetectionPackage = testParams.ambientContextDetectionPackage; + mWearableSensingPackage = testParams.wearableSensingPackage; mConfiguratorPackage = testParams.configuratorPackage; mAppPredictionServicePackage = testParams.appPredictionServicePackage; mIncidentReportApproverPackage = testParams.incidentReportApproverPackage; @@ -2039,6 +2041,9 @@ public class PackageManagerService implements PackageSender, TestUtilityService mAmbientContextDetectionPackage = ensureSystemPackageName(computer, getPackageFromComponentString( R.string.config_defaultAmbientContextDetectionService)); + mWearableSensingPackage = ensureSystemPackageName(computer, + getPackageFromComponentString( + R.string.config_defaultWearableSensingService)); // Now that we know all of the shared libraries, update all clients to have // the correct library paths. @@ -6031,6 +6036,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService mConfiguratorPackage, mIncidentReportApproverPackage, mAmbientContextDetectionPackage, + mWearableSensingPackage, mAppPredictionServicePackage, COMPANION_PACKAGE_NAME, mRetailDemoPackage, @@ -7064,6 +7070,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService mConfiguratorPackage, mIncidentReportApproverPackage, mAmbientContextDetectionPackage, + mWearableSensingPackage, mAppPredictionServicePackage, COMPANION_PACKAGE_NAME, mRetailDemoPackage, diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java index 4391fdd51c5a5..bffbb84bcfae0 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java @@ -94,6 +94,7 @@ public final class PackageManagerServiceTestParams { public @Nullable String retailDemoPackage; public @Nullable String recentsPackage; public @Nullable String ambientContextDetectionPackage; + public @Nullable String wearableSensingPackage; public ComponentName resolveComponentName; public ArrayMap packages; public boolean enableFreeCacheV2; diff --git a/services/core/java/com/android/server/wearable/OWNERS b/services/core/java/com/android/server/wearable/OWNERS new file mode 100644 index 0000000000000..073e2d79850b2 --- /dev/null +++ b/services/core/java/com/android/server/wearable/OWNERS @@ -0,0 +1,3 @@ +charliewang@google.com +oni@google.com +volnov@google.com \ No newline at end of file diff --git a/services/core/java/com/android/server/wearable/RemoteWearableSensingService.java b/services/core/java/com/android/server/wearable/RemoteWearableSensingService.java new file mode 100644 index 0000000000000..b2bbcda862ecf --- /dev/null +++ b/services/core/java/com/android/server/wearable/RemoteWearableSensingService.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2022 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 com.android.server.wearable; + +import static android.content.Context.BIND_FOREGROUND_SERVICE; +import static android.content.Context.BIND_INCLUDE_CAPABILITIES; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.os.ParcelFileDescriptor; +import android.os.PersistableBundle; +import android.os.RemoteCallback; +import android.os.SharedMemory; +import android.service.wearable.IWearableSensingService; +import android.service.wearable.WearableSensingService; +import android.util.Slog; + +import com.android.internal.infra.ServiceConnector; + +/** Manages the connection to the remote wearable sensing service. */ +final class RemoteWearableSensingService extends ServiceConnector.Impl { + private static final String TAG = + com.android.server.wearable.RemoteWearableSensingService.class.getSimpleName(); + private final static boolean DEBUG = false; + + RemoteWearableSensingService(Context context, ComponentName serviceName, + int userId) { + super(context, new Intent( + WearableSensingService.SERVICE_INTERFACE).setComponent(serviceName), + BIND_FOREGROUND_SERVICE | BIND_INCLUDE_CAPABILITIES, userId, + IWearableSensingService.Stub::asInterface); + + // Bind right away + connect(); + } + + @Override + protected long getAutoDisconnectTimeoutMs() { + // Disable automatic unbinding. + return -1; + } + + /** + * Provides the implementation a data stream to the wearable. + * + * @param parcelFileDescriptor The data stream to the wearable + * @param callback The callback for service status + */ + public void provideDataStream(ParcelFileDescriptor parcelFileDescriptor, + RemoteCallback callback) { + if (DEBUG) { + Slog.i(TAG, "Providing data stream."); + } + post(service -> service.provideDataStream(parcelFileDescriptor, callback)); + } + + /** + * Provides the implementation data. + * + * @param data Application configuration data to provide to the implementation. + * @param sharedMemory The unrestricted data blob to provide to the implementation. + * @param callback The callback for service status + */ + public void provideData(PersistableBundle data, + SharedMemory sharedMemory, + RemoteCallback callback) { + if (DEBUG) { + Slog.i(TAG, "Providing data."); + } + post(service -> service.provideData(data, sharedMemory, callback)); + } +} diff --git a/services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java b/services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java new file mode 100644 index 0000000000000..e73fd0f400acc --- /dev/null +++ b/services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2022 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 com.android.server.wearable; + +import android.Manifest; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.app.AppGlobals; +import android.app.ambientcontext.AmbientContextEvent; +import android.app.wearable.WearableSensingManager; +import android.content.ComponentName; +import android.content.pm.PackageManager; +import android.content.pm.ServiceInfo; +import android.os.Bundle; +import android.system.OsConstants; +import android.os.ParcelFileDescriptor; +import android.os.PersistableBundle; +import android.os.RemoteCallback; +import android.os.RemoteException; +import android.os.SharedMemory; +import android.util.IndentingPrintWriter; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.infra.AbstractPerUserSystemService; + +import java.io.PrintWriter; + +/** + * Per-user manager service for managing sensing {@link AmbientContextEvent}s on Wearables. + */ +final class WearableSensingManagerPerUserService extends + AbstractPerUserSystemService { + private static final String TAG = WearableSensingManagerPerUserService.class.getSimpleName(); + + @Nullable + @VisibleForTesting + RemoteWearableSensingService mRemoteService; + + private ComponentName mComponentName; + + WearableSensingManagerPerUserService( + @NonNull WearableSensingManagerService master, Object lock, @UserIdInt int userId) { + super(master, lock, userId); + } + + static void notifyStatusCallback(RemoteCallback statusCallback, int statusCode) { + Bundle bundle = new Bundle(); + bundle.putInt( + WearableSensingManager.STATUS_RESPONSE_BUNDLE_KEY, statusCode); + statusCallback.sendResult(bundle); + } + + void destroyLocked() { + Slog.d(TAG, "Trying to cancel the remote request. Reason: Service destroyed."); + if (mRemoteService != null) { + synchronized (mLock) { + mRemoteService.unbind(); + mRemoteService = null; + } + } + } + + @GuardedBy("mLock") + private void ensureRemoteServiceInitiated() { + if (mRemoteService == null) { + mRemoteService = new RemoteWearableSensingService( + getContext(), mComponentName, getUserId()); + } + } + + /** + * get the currently bound component name. + */ + @VisibleForTesting + ComponentName getComponentName() { + return mComponentName; + } + + + /** + * Resolves and sets up the service if it had not been done yet. Returns true if the service + * is available. + */ + @GuardedBy("mLock") + @VisibleForTesting + boolean setUpServiceIfNeeded() { + if (mComponentName == null) { + mComponentName = updateServiceInfoLocked(); + } + if (mComponentName == null) { + return false; + } + + ServiceInfo serviceInfo; + try { + serviceInfo = AppGlobals.getPackageManager().getServiceInfo( + mComponentName, 0, mUserId); + } catch (RemoteException e) { + Slog.w(TAG, "RemoteException while setting up service"); + return false; + } + return serviceInfo != null; + } + + @Override + protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent) + throws PackageManager.NameNotFoundException { + ServiceInfo serviceInfo; + try { + serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent, + 0, mUserId); + if (serviceInfo != null) { + final String permission = serviceInfo.permission; + if (!Manifest.permission.BIND_WEARABLE_SENSING_SERVICE.equals( + permission)) { + throw new SecurityException(String.format( + "Service %s requires %s permission. Found %s permission", + serviceInfo.getComponentName(), + Manifest.permission.BIND_WEARABLE_SENSING_SERVICE, + serviceInfo.permission)); + } + } + } catch (RemoteException e) { + throw new PackageManager.NameNotFoundException( + "Could not get service for " + serviceComponent); + } + return serviceInfo; + } + + @Override + protected void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) { + synchronized (super.mLock) { + super.dumpLocked(prefix, pw); + } + if (mRemoteService != null) { + mRemoteService.dump("", new IndentingPrintWriter(pw, " ")); + } + } + + /** + * Handles sending the provided data stream for the wearable to the wearable sensing service. + */ + public void onProvideDataStream( + ParcelFileDescriptor parcelFileDescriptor, + RemoteCallback callback) { + Slog.i(TAG, "onProvideDataStream in per user service."); + synchronized (mLock) { + if (!setUpServiceIfNeeded()) { + Slog.w(TAG, "Detection service is not available at this moment."); + notifyStatusCallback(callback, WearableSensingManager.STATUS_SERVICE_UNAVAILABLE); + return; + } + Slog.i(TAG, "calling over to remote servvice."); + ensureRemoteServiceInitiated(); + mRemoteService.provideDataStream(parcelFileDescriptor, callback); + } + } + + /** + * Handles sending the provided data to the wearable sensing service. + */ + public void onProvidedData(PersistableBundle data, + SharedMemory sharedMemory, + RemoteCallback callback) { + synchronized (mLock) { + if (!setUpServiceIfNeeded()) { + Slog.w(TAG, "Detection service is not available at this moment."); + notifyStatusCallback(callback, WearableSensingManager.STATUS_SERVICE_UNAVAILABLE); + return; + } + ensureRemoteServiceInitiated(); + if (sharedMemory != null) { + sharedMemory.setProtect(OsConstants.PROT_READ); + } + mRemoteService.provideData(data, sharedMemory, callback); + } + } +} diff --git a/services/core/java/com/android/server/wearable/WearableSensingManagerService.java b/services/core/java/com/android/server/wearable/WearableSensingManagerService.java new file mode 100644 index 0000000000000..e155a06901286 --- /dev/null +++ b/services/core/java/com/android/server/wearable/WearableSensingManagerService.java @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2022 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 com.android.server.wearable; + +import static android.provider.DeviceConfig.NAMESPACE_WEARABLE_SENSING; + +import android.Manifest; +import android.annotation.NonNull; +import android.annotation.UserIdInt; +import android.app.ambientcontext.AmbientContextEvent; +import android.app.wearable.IWearableSensingManager; +import android.app.wearable.WearableSensingManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.PackageManagerInternal; +import android.os.ParcelFileDescriptor; +import android.os.PersistableBundle; +import android.os.RemoteCallback; +import android.os.ResultReceiver; +import android.os.SharedMemory; +import android.os.ShellCallback; +import android.os.UserHandle; +import android.provider.DeviceConfig; +import android.util.Slog; + +import com.android.internal.R; +import com.android.server.LocalServices; +import com.android.server.SystemService; +import com.android.server.infra.AbstractMasterSystemService; +import com.android.server.infra.FrameworkResourcesServiceNameResolver; +import com.android.server.pm.KnownPackages; + +import java.io.FileDescriptor; +import java.util.Objects; +import java.util.Set; + +/** + * System service for managing sensing {@link AmbientContextEvent}s on Wearables. + */ +public class WearableSensingManagerService extends + AbstractMasterSystemService { + private static final String TAG = WearableSensingManagerService.class.getSimpleName(); + private static final String KEY_SERVICE_ENABLED = "service_enabled"; + + /** Default value in absence of {@link DeviceConfig} override. */ + private static final boolean DEFAULT_SERVICE_ENABLED = true; + public static final int MAX_TEMPORARY_SERVICE_DURATION_MS = 30000; + + private final Context mContext; + volatile boolean mIsServiceEnabled; + + public WearableSensingManagerService(Context context) { + super(context, + new FrameworkResourcesServiceNameResolver( + context, + R.string.config_defaultWearableSensingService), + /*disallowProperty=*/null, + PACKAGE_UPDATE_POLICY_REFRESH_EAGER + | /*To avoid high latency*/ PACKAGE_RESTART_POLICY_REFRESH_EAGER); + mContext = context; + } + + @Override + public void onStart() { + publishBinderService( + Context.WEARABLE_SENSING_SERVICE, new WearableSensingManagerInternal()); + } + + @Override + public void onBootPhase(int phase) { + if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { + DeviceConfig.addOnPropertiesChangedListener( + NAMESPACE_WEARABLE_SENSING, + getContext().getMainExecutor(), + (properties) -> onDeviceConfigChange(properties.getKeyset())); + + mIsServiceEnabled = DeviceConfig.getBoolean( + NAMESPACE_WEARABLE_SENSING, + KEY_SERVICE_ENABLED, DEFAULT_SERVICE_ENABLED); + } + } + + + private void onDeviceConfigChange(@NonNull Set keys) { + if (keys.contains(KEY_SERVICE_ENABLED)) { + mIsServiceEnabled = DeviceConfig.getBoolean( + NAMESPACE_WEARABLE_SENSING, + KEY_SERVICE_ENABLED, DEFAULT_SERVICE_ENABLED); + } + } + + @Override + protected WearableSensingManagerPerUserService newServiceLocked(int resolvedUserId, + boolean disabled) { + return new WearableSensingManagerPerUserService(this, mLock, resolvedUserId); + } + + @Override + protected void onServiceRemoved( + WearableSensingManagerPerUserService service, @UserIdInt int userId) { + Slog.d(TAG, "onServiceRemoved"); + service.destroyLocked(); + } + + @Override + protected void onServicePackageRestartedLocked(@UserIdInt int userId) { + Slog.d(TAG, "onServicePackageRestartedLocked."); + } + + @Override + protected void onServicePackageUpdatedLocked(@UserIdInt int userId) { + Slog.d(TAG, "onServicePackageUpdatedLocked."); + } + + @Override + protected void enforceCallingPermissionForManagement() { + getContext().enforceCallingPermission( + Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT, TAG); + } + + @Override + protected int getMaximumTemporaryServiceDurationMs() { + return MAX_TEMPORARY_SERVICE_DURATION_MS; + } + + /** Returns {@code true} if the detection service is configured on this device. */ + public static boolean isDetectionServiceConfigured() { + final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class); + final String[] packageNames = pmi.getKnownPackageNames( + KnownPackages.PACKAGE_WEARABLE_SENSING, UserHandle.USER_SYSTEM); + boolean isServiceConfigured = (packageNames.length != 0); + Slog.i(TAG, "Wearable sensing service configured: " + isServiceConfigured); + return isServiceConfigured; + } + + /** + * Returns the AmbientContextManagerPerUserService component for this user. + */ + public ComponentName getComponentName(@UserIdInt int userId) { + synchronized (mLock) { + final WearableSensingManagerPerUserService service = getServiceForUserLocked(userId); + if (service != null) { + return service.getComponentName(); + } + } + return null; + } + + private final class WearableSensingManagerInternal extends IWearableSensingManager.Stub { + final WearableSensingManagerPerUserService mService = getServiceForUserLocked( + UserHandle.getCallingUserId()); + + @Override + public void provideDataStream( + ParcelFileDescriptor parcelFileDescriptor, + RemoteCallback callback) { + Slog.i(TAG, "WearableSensingManagerInternal provideDataStream."); + Objects.requireNonNull(parcelFileDescriptor); + Objects.requireNonNull(callback); + mContext.enforceCallingOrSelfPermission( + Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE, TAG); + if (!mIsServiceEnabled) { + Slog.w(TAG, "Service not available."); + WearableSensingManagerPerUserService.notifyStatusCallback(callback, + WearableSensingManager.STATUS_SERVICE_UNAVAILABLE); + return; + } + mService.onProvideDataStream(parcelFileDescriptor, callback); + } + + @Override + public void provideData( + PersistableBundle data, + SharedMemory sharedMemory, + RemoteCallback callback) { + Slog.i(TAG, "WearableSensingManagerInternal provideData."); + Objects.requireNonNull(data); + Objects.requireNonNull(callback); + mContext.enforceCallingOrSelfPermission( + Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE, TAG); + if (!mIsServiceEnabled) { + Slog.w(TAG, "Service not available."); + WearableSensingManagerPerUserService.notifyStatusCallback(callback, + WearableSensingManager.STATUS_SERVICE_UNAVAILABLE); + return; + } + mService.onProvidedData(data, sharedMemory, callback); + } + + @Override + public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, + String[] args, ShellCallback callback, ResultReceiver resultReceiver) { + } + } +}