Merge changes from topic "CM-API-mode"

* changes:
  Add callback registration mechanism for listening to communal state changes.
  Add isCommunalMode() to CommunalManager SystemApi.
This commit is contained in:
Xiaowen Lei
2021-12-09 18:32:06 +00:00
committed by Android (Google) Code Review
13 changed files with 241 additions and 10 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";
@@ -1309,6 +1310,20 @@ package android.app.backup {
}
package android.app.communal {
public final class CommunalManager {
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);
}
}
package android.app.compat {
public final class CompatChanges {
@@ -2491,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,15 +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
@@ -33,10 +39,12 @@ import android.os.RemoteException;
*
* @hide
*/
@SystemService(Context.COMMUNAL_MANAGER_SERVICE)
@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
@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,
@@ -59,15 +67,20 @@ public final class CommunalManager {
@Disabled
public static final long ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT = 200324021L;
/** @hide */
public CommunalManager(ICommunalManager service) {
mService = service;
mCommunalModeListeners = new ArrayMap<CommunalModeListener, ICommunalModeListener>();
}
/**
* Updates whether or not the communal view is currently showing over the lockscreen.
*
* @param isShowing Whether communal view is showing.
*
* @hide
*/
@TestApi
@RequiresPermission(Manifest.permission.WRITE_COMMUNAL_STATE)
public void setCommunalViewShowing(boolean isShowing) {
try {
@@ -76,4 +89,72 @@ public final class CommunalManager {
throw e.rethrowFromSystemServer();
}
}
/**
* Checks whether or not the communal view is currently showing over the lockscreen.
*/
@RequiresPermission(Manifest.permission.READ_COMMUNAL_STATE)
public boolean isCommunalMode() {
try {
return mService.isCommunalMode();
} catch (RemoteException e) {
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,12 +16,17 @@
package android.app.communal;
import android.app.communal.ICommunalModeListener;
/**
* System private API for talking with the communal manager service that handles communal mode
* state.
*
* @hide
*/
oneway interface ICommunalManager {
void setCommunalViewShowing(boolean isShowing);
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,10 +5531,18 @@
<!-- 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
@SystemApi -->
<permission android:name="android.permission.READ_COMMUNAL_STATE"
android:protectionLevel="signature|privileged"/>
<!-- Allows the holder to manage whether the system can bind to services
provided by instant apps. This permission is intended to protect
test/development fucntionality and should be used only in such cases.

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,43 @@ 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);
}
/**
* Checks whether or not we are in communal mode.
*/
@RequiresPermission(Manifest.permission.READ_COMMUNAL_STATE)
@Override
public boolean isCommunalMode() {
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);
}
}
}

View File

@@ -123,6 +123,8 @@ public class CommunalManagerServiceTest {
doNothing().when(mContextSpy).enforceCallingPermission(
eq(Manifest.permission.WRITE_COMMUNAL_STATE), anyString());
doNothing().when(mContextSpy).enforceCallingPermission(
eq(Manifest.permission.READ_COMMUNAL_STATE), anyString());
mService = new CommunalManagerService(mContextSpy);
mService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
@@ -202,6 +204,18 @@ public class CommunalManagerServiceTest {
return new Intent(action, Uri.parse("package:" + packageName));
}
@Test
public void testIsCommunalMode_isTrue() throws RemoteException {
mBinder.setCommunalViewShowing(true);
assertThat(mBinder.isCommunalMode()).isTrue();
}
@Test
public void testIsCommunalMode_isFalse() throws RemoteException {
mBinder.setCommunalViewShowing(false);
assertThat(mBinder.isCommunalMode()).isFalse();
}
@Test
public void testIntercept_unlocked_communalOff_appNotEnabled_showWhenLockedOff() {
when(mKeyguardManager.isKeyguardLocked()).thenReturn(false);