Add IColorDisplayManager
- Add IColorDisplayManager - Add CONTROL_DISPLAY_COLOR_TRANSFORMS permission Bug: 111215474 Test: atest FrameworksServicesTest:ColorDisplayServiceTest Change-Id: Ia8182ccc80c1733f00c62b136e7950e2d2092d75
This commit is contained in:
parent
cc64dadb05
commit
39b0311db8
@ -162,6 +162,7 @@ java_defaults {
|
||||
"core/java/android/hardware/biometrics/IBiometricService.aidl",
|
||||
"core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl",
|
||||
"core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl",
|
||||
"core/java/android/hardware/display/IColorDisplayManager.aidl",
|
||||
"core/java/android/hardware/display/IDisplayManager.aidl",
|
||||
"core/java/android/hardware/display/IDisplayManagerCallback.aidl",
|
||||
"core/java/android/hardware/display/IVirtualDisplayCallback.aidl",
|
||||
|
@ -56,6 +56,7 @@ package android {
|
||||
field public static final java.lang.String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
|
||||
field public static final java.lang.String CONNECTIVITY_INTERNAL = "android.permission.CONNECTIVITY_INTERNAL";
|
||||
field public static final java.lang.String CONNECTIVITY_USE_RESTRICTED_NETWORKS = "android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS";
|
||||
field public static final java.lang.String CONTROL_DISPLAY_COLOR_TRANSFORMS = "android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS";
|
||||
field public static final java.lang.String CONTROL_DISPLAY_SATURATION = "android.permission.CONTROL_DISPLAY_SATURATION";
|
||||
field public static final java.lang.String CONTROL_INCALL_EXPERIENCE = "android.permission.CONTROL_INCALL_EXPERIENCE";
|
||||
field public static final java.lang.String CONTROL_KEYGUARD_SECURE_NOTIFICATIONS = "android.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS";
|
||||
|
@ -59,6 +59,7 @@ import android.hardware.SystemSensorManager;
|
||||
import android.hardware.biometrics.BiometricManager;
|
||||
import android.hardware.biometrics.IBiometricService;
|
||||
import android.hardware.camera2.CameraManager;
|
||||
import android.hardware.display.ColorDisplayManager;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.hardware.face.FaceManager;
|
||||
import android.hardware.face.IFaceService;
|
||||
@ -385,6 +386,14 @@ final class SystemServiceRegistry {
|
||||
return new DisplayManager(ctx.getOuterContext());
|
||||
}});
|
||||
|
||||
registerService(Context.COLOR_DISPLAY_SERVICE, ColorDisplayManager.class,
|
||||
new CachedServiceFetcher<ColorDisplayManager>() {
|
||||
@Override
|
||||
public ColorDisplayManager createService(ContextImpl ctx) {
|
||||
return new ColorDisplayManager();
|
||||
}
|
||||
});
|
||||
|
||||
// InputMethodManager has its own cache strategy based on display id to support apps that
|
||||
// still assume InputMethodManager is a per-process singleton and it's safe to directly
|
||||
// access internal fields via reflection. Hence directly use ServiceFetcher instead of
|
||||
|
@ -3120,6 +3120,7 @@ public abstract class Context {
|
||||
//@hide: HDMI_CONTROL_SERVICE,
|
||||
INPUT_SERVICE,
|
||||
DISPLAY_SERVICE,
|
||||
//@hide COLOR_DISPLAY_SERVICE,
|
||||
USER_SERVICE,
|
||||
RESTRICTIONS_SERVICE,
|
||||
APP_OPS_SERVICE,
|
||||
@ -4099,6 +4100,16 @@ public abstract class Context {
|
||||
*/
|
||||
public static final String DISPLAY_SERVICE = "display";
|
||||
|
||||
/**
|
||||
* Use with {@link #getSystemService(String)} to retrieve a
|
||||
* {@link android.hardware.display.ColorDisplayManager} for controlling color transforms.
|
||||
*
|
||||
* @see #getSystemService(String)
|
||||
* @see android.hardware.display.ColorDisplayManager
|
||||
* @hide
|
||||
*/
|
||||
public static final String COLOR_DISPLAY_SERVICE = "color_display";
|
||||
|
||||
/**
|
||||
* Use with {@link #getSystemService(String)} to retrieve a
|
||||
* {@link android.os.UserManager} for managing users on devices that support multiple users.
|
||||
|
@ -16,20 +16,81 @@
|
||||
|
||||
package android.hardware.display;
|
||||
|
||||
import android.annotation.RequiresPermission;
|
||||
import android.annotation.SystemService;
|
||||
import android.content.Context;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.ServiceManager.ServiceNotFoundException;
|
||||
|
||||
import com.android.internal.R;
|
||||
|
||||
/**
|
||||
* Manages the display's color transforms and modes.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemService(Context.COLOR_DISPLAY_SERVICE)
|
||||
public final class ColorDisplayManager {
|
||||
|
||||
private final ColorDisplayManagerInternal mManager;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public ColorDisplayManager() {
|
||||
mManager = ColorDisplayManagerInternal.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the device has a wide color gamut display.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
|
||||
public boolean isDeviceColorManaged() {
|
||||
return mManager.isDeviceColorManaged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if Night Display is supported by the device.
|
||||
*/
|
||||
public static boolean isNightDisplayAvailable(Context context) {
|
||||
return context.getResources().getBoolean(R.bool.config_nightDisplayAvailable);
|
||||
}
|
||||
|
||||
private static class ColorDisplayManagerInternal {
|
||||
|
||||
private static ColorDisplayManagerInternal sInstance;
|
||||
|
||||
private final IColorDisplayManager mCdm;
|
||||
|
||||
private ColorDisplayManagerInternal(IColorDisplayManager colorDisplayManager) {
|
||||
mCdm = colorDisplayManager;
|
||||
}
|
||||
|
||||
public static ColorDisplayManagerInternal getInstance() {
|
||||
synchronized (ColorDisplayManagerInternal.class) {
|
||||
if (sInstance == null) {
|
||||
try {
|
||||
IBinder b = ServiceManager.getServiceOrThrow(Context.COLOR_DISPLAY_SERVICE);
|
||||
sInstance = new ColorDisplayManagerInternal(
|
||||
IColorDisplayManager.Stub.asInterface(b));
|
||||
} catch (ServiceNotFoundException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
}
|
||||
|
||||
boolean isDeviceColorManaged() {
|
||||
try {
|
||||
return mCdm.isDeviceColorManaged();
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
22
core/java/android/hardware/display/IColorDisplayManager.aidl
Normal file
22
core/java/android/hardware/display/IColorDisplayManager.aidl
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.hardware.display;
|
||||
|
||||
/** @hide */
|
||||
interface IColorDisplayManager {
|
||||
boolean isDeviceColorManaged();
|
||||
}
|
@ -3321,6 +3321,13 @@
|
||||
<permission android:name="android.permission.CONTROL_DISPLAY_SATURATION"
|
||||
android:protectionLevel="signature|privileged" />
|
||||
|
||||
<!-- Allows an application to control display color transformations.
|
||||
<p>Not for use by third-party applications.</p>
|
||||
@hide
|
||||
@SystemApi -->
|
||||
<permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS"
|
||||
android:protectionLevel="signature|privileged" />
|
||||
|
||||
<!-- Allows an application to collect usage infomation about brightness slider changes.
|
||||
<p>Not for use by third-party applications.</p>
|
||||
@hide
|
||||
|
@ -281,6 +281,7 @@ applications that come with the platform
|
||||
<permission name="android.permission.WRITE_APN_SETTINGS"/>
|
||||
<permission name="android.permission.WRITE_MEDIA_STORAGE"/>
|
||||
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
|
||||
<permission name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
|
||||
</privapp-permissions>
|
||||
|
||||
<privapp-permissions package="com.android.settings.intelligence">
|
||||
@ -408,6 +409,7 @@ applications that come with the platform
|
||||
<permission name="android.permission.WRITE_MEDIA_STORAGE"/>
|
||||
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
|
||||
<permission name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"/>
|
||||
<permission name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
|
||||
</privapp-permissions>
|
||||
|
||||
<privapp-permissions package="com.android.tv">
|
||||
|
@ -223,6 +223,9 @@
|
||||
|
||||
<uses-permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS" />
|
||||
|
||||
<!-- Permission to change the display color -->
|
||||
<uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
|
||||
|
||||
<protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
|
||||
<protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" />
|
||||
<protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" />
|
||||
|
@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.server.display;
|
||||
|
||||
import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.TypeEvaluator;
|
||||
@ -29,8 +31,10 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.database.ContentObserver;
|
||||
import android.hardware.display.IColorDisplayManager;
|
||||
import android.net.Uri;
|
||||
import android.opengl.Matrix;
|
||||
import android.os.Binder;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.UserHandle;
|
||||
@ -39,6 +43,7 @@ import android.util.MathUtils;
|
||||
import android.util.Slog;
|
||||
import android.view.animation.AnimationUtils;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.app.ColorDisplayController;
|
||||
import com.android.server.SystemService;
|
||||
import com.android.server.twilight.TwilightListener;
|
||||
@ -49,12 +54,8 @@ import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.ZoneId;
|
||||
|
||||
import com.android.internal.R;
|
||||
|
||||
import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
|
||||
|
||||
/**
|
||||
* Tints the display at night.
|
||||
* Controls the display's color transforms.
|
||||
*/
|
||||
public final class ColorDisplayService extends SystemService
|
||||
implements ColorDisplayController.Callback {
|
||||
@ -101,7 +102,7 @@ public final class ColorDisplayService extends SystemService
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
// Nothing to publish.
|
||||
publishBinderService(Context.COLOR_DISPLAY_SERVICE, new BinderService());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -171,7 +172,7 @@ public final class ColorDisplayService extends SystemService
|
||||
}
|
||||
};
|
||||
cr.registerContentObserver(Secure.getUriFor(Secure.USER_SETUP_COMPLETE),
|
||||
false /* notifyForDescendents */, mUserSetupObserver, mCurrentUser);
|
||||
false /* notifyForDescendants */, mUserSetupObserver, mCurrentUser);
|
||||
} else if (mBootCompleted) {
|
||||
setUp();
|
||||
}
|
||||
@ -405,8 +406,8 @@ public final class ColorDisplayService extends SystemService
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first date time corresponding to the local time that occurs before the
|
||||
* provided date time.
|
||||
* Returns the first date time corresponding to the local time that occurs before the provided
|
||||
* date time.
|
||||
*
|
||||
* @param compareTime the LocalDateTime to compare against
|
||||
* @return the prior LocalDateTime corresponding to this local time
|
||||
@ -420,8 +421,8 @@ public final class ColorDisplayService extends SystemService
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first date time corresponding to this local time that occurs after the
|
||||
* provided date time.
|
||||
* Returns the first date time corresponding to this local time that occurs after the provided
|
||||
* date time.
|
||||
*
|
||||
* @param compareTime the LocalDateTime to compare against
|
||||
* @return the next LocalDateTime corresponding to this local time
|
||||
@ -434,6 +435,11 @@ public final class ColorDisplayService extends SystemService
|
||||
return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt;
|
||||
}
|
||||
|
||||
private boolean isDeviceColorManagedInternal() {
|
||||
final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
|
||||
return dtm.isDeviceColorManaged();
|
||||
}
|
||||
|
||||
private abstract class AutoMode implements ColorDisplayController.Callback {
|
||||
public abstract void onStart();
|
||||
|
||||
@ -616,4 +622,16 @@ public final class ColorDisplayService extends SystemService
|
||||
return mResultMatrix;
|
||||
}
|
||||
}
|
||||
|
||||
private final class BinderService extends IColorDisplayManager.Stub {
|
||||
@Override
|
||||
public boolean isDeviceColorManaged() {
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
return isDeviceColorManagedInternal();
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
package com.android.server.display;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityTaskManager;
|
||||
import android.opengl.Matrix;
|
||||
import android.os.IBinder;
|
||||
@ -27,8 +26,10 @@ import android.os.SystemProperties;
|
||||
import android.util.Log;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.app.ColorDisplayController;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
@ -59,10 +60,6 @@ public class DisplayTransformManager {
|
||||
|
||||
private static final int SURFACE_FLINGER_TRANSACTION_COLOR_MATRIX = 1015;
|
||||
private static final int SURFACE_FLINGER_TRANSACTION_DALTONIZER = 1014;
|
||||
|
||||
private static final String PERSISTENT_PROPERTY_SATURATION = "persist.sys.sf.color_saturation";
|
||||
private static final String PERSISTENT_PROPERTY_DISPLAY_COLOR = "persist.sys.sf.native_mode";
|
||||
|
||||
/**
|
||||
* SurfaceFlinger global saturation factor.
|
||||
*/
|
||||
@ -71,6 +68,10 @@ public class DisplayTransformManager {
|
||||
* SurfaceFlinger display color (managed, unmanaged, etc.).
|
||||
*/
|
||||
private static final int SURFACE_FLINGER_TRANSACTION_DISPLAY_COLOR = 1023;
|
||||
private static final int SURFACE_FLINGER_TRANSACTION_QUERY_WIDE_COLOR = 1030;
|
||||
|
||||
private static final String PERSISTENT_PROPERTY_SATURATION = "persist.sys.sf.color_saturation";
|
||||
private static final String PERSISTENT_PROPERTY_DISPLAY_COLOR = "persist.sys.sf.native_mode";
|
||||
|
||||
private static final float COLOR_SATURATION_NATURAL = 1.0f;
|
||||
private static final float COLOR_SATURATION_BOOSTED = 1.1f;
|
||||
@ -268,6 +269,29 @@ public class DisplayTransformManager {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the screen is wide color gamut via SurfaceFlinger's
|
||||
* {@link #SURFACE_FLINGER_TRANSACTION_QUERY_WIDE_COLOR}.
|
||||
*/
|
||||
public boolean isDeviceColorManaged() {
|
||||
final IBinder flinger = ServiceManager.getService(SURFACE_FLINGER);
|
||||
if (flinger != null) {
|
||||
final Parcel data = Parcel.obtain();
|
||||
final Parcel reply = Parcel.obtain();
|
||||
data.writeInterfaceToken("android.ui.ISurfaceComposer");
|
||||
try {
|
||||
flinger.transact(SURFACE_FLINGER_TRANSACTION_QUERY_WIDE_COLOR, data, reply, 0);
|
||||
return reply.readBoolean();
|
||||
} catch (RemoteException ex) {
|
||||
Log.e(TAG, "Failed to query wide color support", ex);
|
||||
} finally {
|
||||
data.recycle();
|
||||
reply.recycle();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Propagates the provided saturation to the SurfaceFlinger.
|
||||
*/
|
||||
|
@ -66,6 +66,7 @@
|
||||
<uses-permission android:name="android.permission.SUSPEND_APPS"/>
|
||||
<uses-permission android:name="android.permission.CONTROL_KEYGUARD"/>
|
||||
<uses-permission android:name="android.permission.MANAGE_BIND_INSTANT_SERVICE"/>
|
||||
<uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
|
||||
|
||||
<!-- Uses API introduced in O (26) -->
|
||||
<uses-sdk android:minSdkVersion="1"
|
||||
|
@ -35,6 +35,7 @@ import android.test.mock.MockContentResolver;
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.app.ColorDisplayController;
|
||||
import com.android.internal.util.test.FakeSettingsProvider;
|
||||
import com.android.server.LocalServices;
|
||||
@ -911,7 +912,11 @@ public class ColorDisplayServiceTest {
|
||||
startService();
|
||||
assertAccessibilityTransformActivated(true /* activated */ );
|
||||
assertUserColorMode(ColorDisplayController.COLOR_MODE_NATURAL);
|
||||
assertActiveColorMode(ColorDisplayController.COLOR_MODE_SATURATED);
|
||||
if (isColorModeValid(ColorDisplayController.COLOR_MODE_SATURATED)) {
|
||||
assertActiveColorMode(ColorDisplayController.COLOR_MODE_SATURATED);
|
||||
} else if (isColorModeValid(ColorDisplayController.COLOR_MODE_AUTOMATIC)) {
|
||||
assertActiveColorMode(ColorDisplayController.COLOR_MODE_AUTOMATIC);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -926,7 +931,11 @@ public class ColorDisplayServiceTest {
|
||||
startService();
|
||||
assertAccessibilityTransformActivated(true /* activated */ );
|
||||
assertUserColorMode(ColorDisplayController.COLOR_MODE_NATURAL);
|
||||
assertActiveColorMode(ColorDisplayController.COLOR_MODE_SATURATED);
|
||||
if (isColorModeValid(ColorDisplayController.COLOR_MODE_SATURATED)) {
|
||||
assertActiveColorMode(ColorDisplayController.COLOR_MODE_SATURATED);
|
||||
} else if (isColorModeValid(ColorDisplayController.COLOR_MODE_AUTOMATIC)) {
|
||||
assertActiveColorMode(ColorDisplayController.COLOR_MODE_AUTOMATIC);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -942,7 +951,11 @@ public class ColorDisplayServiceTest {
|
||||
startService();
|
||||
assertAccessibilityTransformActivated(true /* activated */ );
|
||||
assertUserColorMode(ColorDisplayController.COLOR_MODE_NATURAL);
|
||||
assertActiveColorMode(ColorDisplayController.COLOR_MODE_SATURATED);
|
||||
if (isColorModeValid(ColorDisplayController.COLOR_MODE_SATURATED)) {
|
||||
assertActiveColorMode(ColorDisplayController.COLOR_MODE_SATURATED);
|
||||
} else if (isColorModeValid(ColorDisplayController.COLOR_MODE_AUTOMATIC)) {
|
||||
assertActiveColorMode(ColorDisplayController.COLOR_MODE_AUTOMATIC);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -1029,6 +1042,24 @@ public class ColorDisplayServiceTest {
|
||||
mColorDisplayController.setColorMode(mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the color mode is valid on the device the tests are running on.
|
||||
*
|
||||
* @param mode the mode to check
|
||||
*/
|
||||
private boolean isColorModeValid(int mode) {
|
||||
final int[] availableColorModes = mContext.getResources().getIntArray(
|
||||
R.array.config_availableColorModes);
|
||||
if (availableColorModes != null) {
|
||||
for (int availableMode : availableColorModes) {
|
||||
if (mode == availableMode) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to start {@link #mColorDisplayService}.
|
||||
*/
|
||||
@ -1038,7 +1069,6 @@ public class ColorDisplayServiceTest {
|
||||
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mColorDisplayService.onStart();
|
||||
mColorDisplayService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
|
||||
mColorDisplayService.onStartUser(mUserId);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user