Add callback registration mechanism for listening to communal state changes.

Design: go/communal-manager-api.

Note: this CL also removed "_MANAGER" from the communal service
constant, and "_manager" from the communal service name. When these
became unhidden, ApiLint.kt suggested the changes.
  - http://cs/android/tools/metalava/src/main/java/com/android/tools/metalava/ApiLint.kt;l=2369-2386;rcl=0ef26a465a89c45fcfd24d648883208cd0161fd2

Ignore-AOSP-First: tied to launch of new upcoming hardware.

Test: atest CtsAppTestCases:CommunalManagerTest
Test: atest FrameworksMockingServicesTests:CommunalManagerServiceTest

Bug: 206054365
Change-Id: I6d225d6ddb482d9cffb16d1f9a25a98abd0cc382
This commit is contained in:
Xiaowen Lei
2021-11-16 01:19:29 +00:00
parent ef06f6dbaa
commit a676d9b3df
12 changed files with 188 additions and 15 deletions

View File

@@ -224,6 +224,7 @@ package android {
field public static final String READ_APP_SPECIFIC_LOCALES = "android.permission.READ_APP_SPECIFIC_LOCALES";
field public static final String READ_CARRIER_APP_INFO = "android.permission.READ_CARRIER_APP_INFO";
field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
field public static final String READ_COMMUNAL_STATE = "android.permission.READ_COMMUNAL_STATE";
field public static final String READ_CONTENT_RATING_SYSTEMS = "android.permission.READ_CONTENT_RATING_SYSTEMS";
field public static final String READ_DEVICE_CONFIG = "android.permission.READ_DEVICE_CONFIG";
field public static final String READ_DREAM_STATE = "android.permission.READ_DREAM_STATE";
@@ -1312,7 +1313,13 @@ package android.app.backup {
package android.app.communal {
public final class CommunalManager {
method @RequiresPermission("android.permission.READ_COMMUNAL_STATE") public boolean isCommunalMode();
method @RequiresPermission(android.Manifest.permission.READ_COMMUNAL_STATE) public void addCommunalModeListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.communal.CommunalManager.CommunalModeListener);
method @RequiresPermission(android.Manifest.permission.READ_COMMUNAL_STATE) public boolean isCommunalMode();
method @RequiresPermission(android.Manifest.permission.READ_COMMUNAL_STATE) public void removeCommunalModeListener(@NonNull android.app.communal.CommunalManager.CommunalModeListener);
}
@java.lang.FunctionalInterface public static interface CommunalManager.CommunalModeListener {
method public void onCommunalModeChanged(boolean);
}
}
@@ -2499,6 +2506,7 @@ package android.content {
field public static final String BATTERY_STATS_SERVICE = "batterystats";
field @Deprecated public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 1048576; // 0x100000
field public static final int BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND = 262144; // 0x40000
field public static final String COMMUNAL_SERVICE = "communal";
field public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions";
field public static final String CONTEXTHUB_SERVICE = "contexthub";
field public static final String ETHERNET_SERVICE = "ethernet";

View File

@@ -42,6 +42,7 @@ package android {
field public static final String TEST_BIOMETRIC = "android.permission.TEST_BIOMETRIC";
field public static final String TEST_MANAGE_ROLLBACKS = "android.permission.TEST_MANAGE_ROLLBACKS";
field public static final String UPGRADE_RUNTIME_PERMISSIONS = "android.permission.UPGRADE_RUNTIME_PERMISSIONS";
field public static final String WRITE_COMMUNAL_STATE = "android.permission.WRITE_COMMUNAL_STATE";
field public static final String WRITE_DEVICE_CONFIG = "android.permission.WRITE_DEVICE_CONFIG";
field @Deprecated public static final String WRITE_MEDIA_STORAGE = "android.permission.WRITE_MEDIA_STORAGE";
field public static final String WRITE_OBB = "android.permission.WRITE_OBB";
@@ -608,6 +609,14 @@ package android.app.blob {
}
package android.app.communal {
public final class CommunalManager {
method @RequiresPermission(android.Manifest.permission.WRITE_COMMUNAL_STATE) public void setCommunalViewShowing(boolean);
}
}
package android.app.contentsuggestions {
public final class ContentSuggestionsManager {
@@ -819,6 +828,7 @@ package android.content.pm {
method public void holdLock(android.os.IBinder, int);
method @RequiresPermission(android.Manifest.permission.KEEP_UNINSTALLED_PACKAGES) public void setKeepUninstalledPackages(@NonNull java.util.List<java.lang.String>);
field public static final String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage";
field public static final String FEATURE_COMMUNAL_MODE = "android.software.communal_mode";
field public static final String FEATURE_FILE_BASED_ENCRYPTION = "android.software.file_based_encryption";
field public static final String FEATURE_HDMI_CEC = "android.hardware.hdmi.cec";
field public static final int FLAG_PERMISSION_REVOKE_WHEN_REQUESTED = 128; // 0x80

View File

@@ -1513,7 +1513,7 @@ public final class SystemServiceRegistry {
}
});
registerService(Context.COMMUNAL_MANAGER_SERVICE, CommunalManager.class,
registerService(Context.COMMUNAL_SERVICE, CommunalManager.class,
new CachedServiceFetcher<CommunalManager>() {
@Override
public CommunalManager createService(ContextImpl ctx) {
@@ -1522,7 +1522,7 @@ public final class SystemServiceRegistry {
return null;
}
IBinder iBinder =
ServiceManager.getService(Context.COMMUNAL_MANAGER_SERVICE);
ServiceManager.getService(Context.COMMUNAL_SERVICE);
return iBinder != null ? new CommunalManager(
ICommunalManager.Stub.asInterface(iBinder)) : null;
}

View File

@@ -17,16 +17,21 @@
package android.app.communal;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
import android.compat.annotation.Overridable;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.RemoteException;
import android.util.ArrayMap;
import java.util.concurrent.Executor;
/**
* System private class for talking with the
@@ -35,10 +40,11 @@ import android.os.RemoteException;
* @hide
*/
@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
@SystemService(Context.COMMUNAL_MANAGER_SERVICE)
@SystemService(Context.COMMUNAL_SERVICE)
@RequiresFeature(PackageManager.FEATURE_COMMUNAL_MODE)
public final class CommunalManager {
private final ICommunalManager mService;
private final ArrayMap<CommunalModeListener, ICommunalModeListener> mCommunalModeListeners;
/**
* This change id is used to annotate packages which can run in communal mode by default,
@@ -64,6 +70,7 @@ public final class CommunalManager {
/** @hide */
public CommunalManager(ICommunalManager service) {
mService = service;
mCommunalModeListeners = new ArrayMap<CommunalModeListener, ICommunalModeListener>();
}
/**
@@ -73,6 +80,7 @@ public final class CommunalManager {
*
* @hide
*/
@TestApi
@RequiresPermission(Manifest.permission.WRITE_COMMUNAL_STATE)
public void setCommunalViewShowing(boolean isShowing) {
try {
@@ -83,7 +91,7 @@ public final class CommunalManager {
}
/**
* Check whether or not the communal view is currently showing over the lockscreen.
* Checks whether or not the communal view is currently showing over the lockscreen.
*/
@RequiresPermission(Manifest.permission.READ_COMMUNAL_STATE)
public boolean isCommunalMode() {
@@ -93,4 +101,60 @@ public final class CommunalManager {
throw e.rethrowFromSystemServer();
}
}
/**
* Listener for communal state changes.
*/
@FunctionalInterface
public interface CommunalModeListener {
/**
* Callback function that executes when the communal state changes.
*/
void onCommunalModeChanged(boolean isCommunalMode);
}
/**
* Registers a callback to execute when the communal state changes.
*
* @param listener The listener to add to receive communal state changes.
* @param executor {@link Executor} to dispatch to. To dispatch the callback to the main
* thread of your application, use
* {@link android.content.Context#getMainExecutor()}.
*/
@RequiresPermission(Manifest.permission.READ_COMMUNAL_STATE)
public void addCommunalModeListener(@NonNull Executor executor,
@NonNull CommunalModeListener listener) {
synchronized (mCommunalModeListeners) {
try {
ICommunalModeListener iListener = new ICommunalModeListener.Stub() {
@Override
public void onCommunalModeChanged(boolean isCommunalMode) {
executor.execute(() -> listener.onCommunalModeChanged(isCommunalMode));
}
};
mService.addCommunalModeListener(iListener);
mCommunalModeListeners.put(listener, iListener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
/**
* Unregisters a callback that executes when communal state changes.
*/
@RequiresPermission(Manifest.permission.READ_COMMUNAL_STATE)
public void removeCommunalModeListener(@NonNull CommunalModeListener listener) {
synchronized (mCommunalModeListeners) {
ICommunalModeListener iListener = mCommunalModeListeners.get(listener);
if (iListener != null) {
try {
mService.removeCommunalModeListener(iListener);
mCommunalModeListeners.remove(listener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
}
}

View File

@@ -16,6 +16,8 @@
package android.app.communal;
import android.app.communal.ICommunalModeListener;
/**
* System private API for talking with the communal manager service that handles communal mode
* state.
@@ -25,4 +27,6 @@ package android.app.communal;
interface ICommunalManager {
oneway void setCommunalViewShowing(boolean isShowing);
boolean isCommunalMode();
void addCommunalModeListener(in ICommunalModeListener listener);
void removeCommunalModeListener(in ICommunalModeListener listener);
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright (C) 2021 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.communal;
/**
* System private API to be notified about communal mode changes.
*
* @hide
*/
oneway interface ICommunalModeListener {
void onCommunalModeChanged(boolean isCommunalMode);
}

View File

@@ -5862,13 +5862,14 @@ public abstract class Context {
/**
* Use with {@link #getSystemService(String)} to retrieve a
* {@link android.app.CommunalManager} for interacting with the global system state.
* {@link android.app.communal.CommunalManager} for interacting with the global system state.
*
* @see #getSystemService(String)
* @see android.app.CommunalManager
* @see android.app.communal.CommunalManager
* @hide
*/
public static final String COMMUNAL_MANAGER_SERVICE = "communal_manager";
@SystemApi
public static final String COMMUNAL_SERVICE = "communal";
/**
* Use with {@link #getSystemService(String)} to retrieve a

View File

@@ -3981,6 +3981,7 @@ public abstract class PackageManager {
* @hide
*/
@SdkConstant(SdkConstantType.FEATURE)
@TestApi
public static final String FEATURE_COMMUNAL_MODE = "android.software.communal_mode";
/** @hide */

View File

@@ -5531,13 +5531,15 @@
<!-- Allows an application to interact with the currently active
{@link com.android.server.communal.CommunalManagerService}.
@hide -->
@hide
@TestApi -->
<permission android:name="android.permission.WRITE_COMMUNAL_STATE"
android:protectionLevel="signature" />
<!-- Allows an application to view information from the currently active
{@link com.android.server.communal.CommunalManagerService}.
@hide -->
{@link com.android.server.communal.CommunalManagerService}.
@hide
@SystemApi -->
<permission android:name="android.permission.READ_COMMUNAL_STATE"
android:protectionLevel="signature|privileged"/>

View File

@@ -519,6 +519,9 @@ applications that come with the platform
<permission name="android.permission.LOCK_DEVICE" />
<!-- Permission required for CTS test - CtsSafetyCenterTestCases -->
<permission name="android.permission.SEND_SAFETY_CENTER_UPDATE" />
<!-- Permission required for CTS test - CommunalManagerTest -->
<permission name="android.permission.WRITE_COMMUNAL_STATE" />
<permission name="android.permission.READ_COMMUNAL_STATE" />
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">

View File

@@ -606,6 +606,10 @@
<!-- Permission required for CTS test - CtsSafetyCenterTestCases -->
<uses-permission android:name="android.permission.SEND_SAFETY_CENTER_UPDATE" />
<!-- Permission required for CTS test - CommunalManagerTest -->
<uses-permission android:name="android.permission.WRITE_COMMUNAL_STATE" />
<uses-permission android:name="android.permission.READ_COMMUNAL_STATE" />
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"

View File

@@ -30,6 +30,7 @@ import android.annotation.RequiresPermission;
import android.app.KeyguardManager;
import android.app.PendingIntent;
import android.app.communal.ICommunalManager;
import android.app.communal.ICommunalModeListener;
import android.app.compat.CompatChanges;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -42,6 +43,8 @@ import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.dreams.DreamManagerInternal;
@@ -77,6 +80,8 @@ public final class CommunalManagerService extends SystemService {
private final PackageReceiver mPackageReceiver;
private final PackageManager mPackageManager;
private final DreamManagerInternal mDreamManagerInternal;
private final RemoteCallbackList<ICommunalModeListener> mListeners =
new RemoteCallbackList<>();
private final ActivityInterceptorCallback mActivityInterceptorCallback =
new ActivityInterceptorCallback() {
@@ -129,7 +134,7 @@ public final class CommunalManagerService extends SystemService {
@Override
public void onStart() {
publishBinderService(Context.COMMUNAL_MANAGER_SERVICE, mBinderService);
publishBinderService(Context.COMMUNAL_SERVICE, mBinderService);
}
@Override
@@ -242,6 +247,27 @@ public final class CommunalManagerService extends SystemService {
return !isAppAllowed(appInfo);
}
private void dispatchCommunalMode(boolean isShowing) {
synchronized (mListeners) {
int i = mListeners.beginBroadcast();
while (i > 0) {
i--;
try {
mListeners.getBroadcastItem(i).onCommunalModeChanged(isShowing);
} catch (RemoteException e) {
// Handled by the RemoteCallbackList.
}
}
mListeners.finishBroadcast();
}
}
private void enforceReadPermission() {
mContext.enforceCallingPermission(Manifest.permission.READ_COMMUNAL_STATE,
Manifest.permission.READ_COMMUNAL_STATE
+ "permission required to read communal state.");
}
private final class BinderService extends ICommunalManager.Stub {
/**
* Sets whether or not we are in communal mode.
@@ -252,7 +278,11 @@ public final class CommunalManagerService extends SystemService {
mContext.enforceCallingPermission(Manifest.permission.WRITE_COMMUNAL_STATE,
Manifest.permission.WRITE_COMMUNAL_STATE
+ "permission required to modify communal state.");
if (mCommunalViewIsShowing.get() == isShowing) {
return;
}
mCommunalViewIsShowing.set(isShowing);
dispatchCommunalMode(isShowing);
}
/**
@@ -261,11 +291,31 @@ public final class CommunalManagerService extends SystemService {
@RequiresPermission(Manifest.permission.READ_COMMUNAL_STATE)
@Override
public boolean isCommunalMode() {
mContext.enforceCallingPermission(Manifest.permission.READ_COMMUNAL_STATE,
Manifest.permission.READ_COMMUNAL_STATE
+ "permission required to read communal state.");
enforceReadPermission();
return mCommunalViewIsShowing.get();
}
/**
* Adds a callback to execute when communal state changes.
*/
@RequiresPermission(Manifest.permission.READ_COMMUNAL_STATE)
public void addCommunalModeListener(ICommunalModeListener listener) {
enforceReadPermission();
synchronized (mListeners) {
mListeners.register(listener);
}
}
/**
* Removes an added callback that execute when communal state changes.
*/
@RequiresPermission(Manifest.permission.READ_COMMUNAL_STATE)
public void removeCommunalModeListener(ICommunalModeListener listener) {
enforceReadPermission();
synchronized (mListeners) {
mListeners.unregister(listener);
}
}
}
/**