Merge "Helper classes for IpcDataCache clients" into tm-dev

This commit is contained in:
Lee Shombert
2022-04-12 14:32:31 +00:00
committed by Android (Google) Code Review
5 changed files with 388 additions and 201 deletions

View File

@@ -313,6 +313,7 @@ public class PropertyInvalidatedCache<Query, Result> {
* one of the permitted values above. The API is a string that is a legal Java simple
* identifier. The api is modified to conform to the system property style guide by
* replacing every upper case letter with an underscore and the lower case equivalent.
* (An initial upper case letter is not prefixed with an underscore).
* There is no requirement that the apiName be the name of an actual API.
*
* Be aware that SystemProperties has a maximum length which is private to the
@@ -326,7 +327,7 @@ public class PropertyInvalidatedCache<Query, Result> {
@NonNull String apiName) {
char[] api = apiName.toCharArray();
int upper = 0;
for (int i = 0; i < api.length; i++) {
for (int i = 1; i < api.length; i++) {
if (Character.isUpperCase(api[i])) {
upper++;
}
@@ -336,7 +337,9 @@ public class PropertyInvalidatedCache<Query, Result> {
for (int i = 0; i < api.length; i++) {
if (Character.isJavaIdentifierPart(api[i])) {
if (Character.isUpperCase(api[i])) {
suffix[j++] = '_';
if (i > 0) {
suffix[j++] = '_';
}
suffix[j++] = Character.toLowerCase(api[i]);
} else {
suffix[j++] = api[i];
@@ -1286,12 +1289,22 @@ public class PropertyInvalidatedCache<Query, Result> {
}
/**
* Return the name of the cache, to be used in debug messages.
* Return the name of the cache, to be used in debug messages. This is exposed
* primarily for testing.
* @hide
*/
private final @NonNull String cacheName() {
public final @NonNull String cacheName() {
return mCacheName;
}
/**
* Return the property used by the cache. This is primarily for test purposes.
* @hide
*/
public final @NonNull String propertyName() {
return mPropertyName;
}
/**
* Return the query as a string, to be used in debug messages. New clients should not
* override this, but should instead add the necessary toString() method to the Query

View File

@@ -185,6 +185,30 @@ public class DevicePolicyManager {
mResourcesManager = new DevicePolicyResourcesManager(context, service);
}
/**
* Fetch the current value of mService. This is used in the binder cache lambda
* expressions.
*/
private final IDevicePolicyManager getService() {
return mService;
}
/**
* Fetch the current value of mParentInstance. This is used in the binder cache
* lambda expressions.
*/
private final boolean isParentInstance() {
return mParentInstance;
}
/**
* Fetch the current value of mContext. This is used in the binder cache lambda
* expressions.
*/
private final Context getContext() {
return mContext;
}
/** @hide test will override it. */
@VisibleForTesting
protected int myUserId() {
@@ -3783,57 +3807,21 @@ public class DevicePolicyManager {
"android.app.extra.RESOURCE_IDS";
/**
* A convenience class that wraps some IpcDataCache methods. Instantiate it with an
* API string. Instances can and should be final static. All instances of this class
* use the same key for invalidation.
* This object is a single place to tack on invalidation and disable calls. All
* binder caches in this class derive from this Config, so all can be invalidated or
* disabled through this Config.
*/
private static class BinderApi {
private final static String KEY = "DevicePolicyManager";
private final String mApi;
BinderApi(String api) {
mApi = api;
}
final String api() {
return mApi;
}
final String key() {
return KEY;
}
final static void invalidate() {
IpcDataCache.invalidateCache(IpcDataCache.MODULE_SYSTEM, KEY);
}
final void disable() {
IpcDataCache.disableForCurrentProcess(mApi);
}
}
private static final IpcDataCache.Config sDpmCaches =
new IpcDataCache.Config(8, IpcDataCache.MODULE_SYSTEM, "DevicePolicyManagerCaches");
/** @hide */
public static void invalidateBinderCaches() {
BinderApi.invalidate();
sDpmCaches.invalidateCache();
}
/**
* A simple wrapper for binder caches in this class. All caches are created with a
* maximum of 8 entries, the SYSTEM module, and a cache name that is the same as the api.
*/
private static class BinderCache<Q,R> extends IpcDataCache<Q,R> {
BinderCache(BinderApi api, IpcDataCache.QueryHandler<Q,R> handler) {
super(8, IpcDataCache.MODULE_SYSTEM, api.key(), api.api(), handler);
}
}
/**
* Disable all caches in the local process.
* @hide
*/
public static void disableLocalProcessCaches() {
disableGetKeyguardDisabledFeaturesCache();
disableHasDeviceOwnerCache();
disableGetProfileOwnerOrDeviceOwnerSupervisionComponentCache();
disableIsOrganizationOwnedDeviceWithManagedProfileCache();
disableGetDeviceOwnerOrganizationNameCache();
disableGetOrganizationNameForUserCache();
disableIsNetworkLoggingEnabled();
/** @hide */
public static void disableLocalCaches() {
sDpmCaches.disableAllForCurrentProcess();
}
/** @hide */
@@ -8435,54 +8423,16 @@ public class DevicePolicyManager {
return getKeyguardDisabledFeatures(admin, myUserId());
}
// A key into the keyguard cache.
private static class KeyguardQuery {
private final ComponentName mAdmin;
private final int mUserHandle;
KeyguardQuery(@Nullable ComponentName admin, int userHandle) {
mAdmin = admin;
mUserHandle = userHandle;
}
public boolean equals(Object o) {
if (o instanceof KeyguardQuery) {
KeyguardQuery r = (KeyguardQuery) o;
return Objects.equals(mAdmin, r.mAdmin) && mUserHandle == r.mUserHandle;
} else {
return false;
}
}
public int hashCode() {
return ((mAdmin != null) ? mAdmin.hashCode() : 0) * 13 + mUserHandle;
}
}
// The query handler does not cache wildcard user IDs, although they should never
// appear in the query.
private static final BinderApi sGetKeyguardDisabledFeatures =
new BinderApi("getKeyguardDisabledFeatures");
private BinderCache<KeyguardQuery, Integer> mGetKeyGuardDisabledFeaturesCache =
new BinderCache<>(sGetKeyguardDisabledFeatures,
new IpcDataCache.QueryHandler<KeyguardQuery, Integer>() {
@Override
public Integer apply(KeyguardQuery query) {
try {
return mService.getKeyguardDisabledFeatures(
query.mAdmin, query.mUserHandle, mParentInstance);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}});
/** @hide */
public static void disableGetKeyguardDisabledFeaturesCache() {
sGetKeyguardDisabledFeatures.disable();
}
private IpcDataCache<Pair<ComponentName, Integer>, Integer> mGetKeyGuardDisabledFeaturesCache =
new IpcDataCache<>(sDpmCaches.child("getKeyguardDisabledFeatures"),
(query) -> getService().getKeyguardDisabledFeatures(
query.first, query.second, isParentInstance()));
/** @hide per-user version */
@UnsupportedAppUsage
public int getKeyguardDisabledFeatures(@Nullable ComponentName admin, int userHandle) {
if (mService != null) {
return mGetKeyGuardDisabledFeaturesCache.query(new KeyguardQuery(admin, userHandle));
return mGetKeyGuardDisabledFeaturesCache.query(new Pair<>(admin, userHandle));
} else {
return KEYGUARD_DISABLE_FEATURES_NONE;
}
@@ -8864,23 +8814,9 @@ public class DevicePolicyManager {
return name != null ? name.getPackageName() : null;
}
private static final BinderApi sHasDeviceOwner =
new BinderApi("hasDeviceOwner");
private BinderCache<Void, Boolean> mHasDeviceOwnerCache =
new BinderCache<>(sHasDeviceOwner,
new IpcDataCache.QueryHandler<Void, Boolean>() {
@Override
public Boolean apply(Void query) {
try {
return mService.hasDeviceOwner();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}});
/** @hide */
public static void disableHasDeviceOwnerCache() {
sHasDeviceOwner.disable();
}
private IpcDataCache<Void, Boolean> mHasDeviceOwnerCache =
new IpcDataCache<>(sDpmCaches.child("hasDeviceOwner"),
(query) -> getService().hasDeviceOwner());
/**
* Called by the system to find out whether the device is managed by a Device Owner.
@@ -9256,25 +9192,10 @@ public class DevicePolicyManager {
return null;
}
private final static BinderApi sGetProfileOwnerOrDeviceOwnerSupervisionComponent =
new BinderApi("getProfileOwnerOrDeviceOwnerSupervisionComponent");
private final BinderCache<UserHandle, ComponentName>
private final IpcDataCache<UserHandle, ComponentName>
mGetProfileOwnerOrDeviceOwnerSupervisionComponentCache =
new BinderCache(sGetProfileOwnerOrDeviceOwnerSupervisionComponent,
new IpcDataCache.QueryHandler<UserHandle, ComponentName>() {
@Override
public ComponentName apply(UserHandle user) {
try {
return mService.getProfileOwnerOrDeviceOwnerSupervisionComponent(
user);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
}});
/** @hide */
public static void disableGetProfileOwnerOrDeviceOwnerSupervisionComponentCache() {
sGetProfileOwnerOrDeviceOwnerSupervisionComponent.disable();
}
new IpcDataCache<>(sDpmCaches.child("getProfileOwnerOrDeviceOwnerSupervisionComponent"),
(arg) -> getService().getProfileOwnerOrDeviceOwnerSupervisionComponent(arg));
/**
* Returns the configured supervision app if it exists and is the device owner or policy owner.
@@ -9329,23 +9250,9 @@ public class DevicePolicyManager {
return null;
}
private final static BinderApi sIsOrganizationOwnedDeviceWithManagedProfile =
new BinderApi("isOrganizationOwnedDeviceWithManagedProfile");
private final BinderCache<Void, Boolean> mIsOrganizationOwnedDeviceWithManagedProfileCache =
new BinderCache(sIsOrganizationOwnedDeviceWithManagedProfile,
new IpcDataCache.QueryHandler<Void, Boolean>() {
@Override
public Boolean apply(Void query) {
try {
return mService.isOrganizationOwnedDeviceWithManagedProfile();
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
}});
/** @hide */
public static void disableIsOrganizationOwnedDeviceWithManagedProfileCache() {
sIsOrganizationOwnedDeviceWithManagedProfile.disable();
}
private final IpcDataCache<Void, Boolean> mIsOrganizationOwnedDeviceWithManagedProfileCache =
new IpcDataCache(sDpmCaches.child("isOrganizationOwnedDeviceWithManagedProfile"),
(query) -> getService().isOrganizationOwnedDeviceWithManagedProfile());
/**
* Apps can use this method to find out if the device was provisioned as
@@ -12927,23 +12834,9 @@ public class DevicePolicyManager {
}
}
private final static BinderApi sGetDeviceOwnerOrganizationName =
new BinderApi("getDeviceOwnerOrganizationName");
private final BinderCache<Void, CharSequence> mGetDeviceOwnerOrganizationNameCache =
new BinderCache(sGetDeviceOwnerOrganizationName,
new IpcDataCache.QueryHandler<Void, CharSequence>() {
@Override
public CharSequence apply(Void query) {
try {
return mService.getDeviceOwnerOrganizationName();
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
}});
/** @hide */
public static void disableGetDeviceOwnerOrganizationNameCache() {
sGetDeviceOwnerOrganizationName.disable();
}
private final IpcDataCache<Void, CharSequence> mGetDeviceOwnerOrganizationNameCache =
new IpcDataCache(sDpmCaches.child("getDeviceOwnerOrganizationName"),
(query) -> getService().getDeviceOwnerOrganizationName());
/**
* Called by the system to retrieve the name of the organization managing the device.
@@ -12960,23 +12853,9 @@ public class DevicePolicyManager {
return mGetDeviceOwnerOrganizationNameCache.query(null);
}
private final static BinderApi sGetOrganizationNameForUser =
new BinderApi("getOrganizationNameForUser");
private final BinderCache<Integer, CharSequence> mGetOrganizationNameForUserCache =
new BinderCache(sGetOrganizationNameForUser,
new IpcDataCache.QueryHandler<Integer, CharSequence>() {
@Override
public CharSequence apply(Integer userHandle) {
try {
return mService.getOrganizationNameForUser(userHandle);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
}});
/** @hide */
public static void disableGetOrganizationNameForUserCache() {
sGetOrganizationNameForUser.disable();
}
private final IpcDataCache<Integer, CharSequence> mGetOrganizationNameForUserCache =
new IpcDataCache<>(sDpmCaches.child("getOrganizationNameForUser"),
(query) -> getService().getOrganizationNameForUser(query));
/**
* Retrieve the default title message used in the confirm credentials screen for a given user.
@@ -13372,24 +13251,10 @@ public class DevicePolicyManager {
}
}
private final static BinderApi sNetworkLoggingApi = new BinderApi("isNetworkLoggingEnabled");
private BinderCache<ComponentName, Boolean> mIsNetworkLoggingEnabledCache =
new BinderCache<>(sNetworkLoggingApi,
new IpcDataCache.QueryHandler<ComponentName, Boolean>() {
@Override
public Boolean apply(ComponentName admin) {
try {
return mService.isNetworkLoggingEnabled(
admin, mContext.getPackageName());
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
}});
/** @hide */
public static void disableIsNetworkLoggingEnabled() {
sNetworkLoggingApi.disable();
}
private IpcDataCache<ComponentName, Boolean> mIsNetworkLoggingEnabledCache =
new IpcDataCache<>(sDpmCaches.child("isNetworkLoggingEnabled"),
(admin) -> getService().isNetworkLoggingEnabled(admin,
getContext().getPackageName()));
/**
* Return whether network logging is enabled by a device owner or profile owner of

View File

@@ -23,7 +23,7 @@ import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.PropertyInvalidatedCache;
import android.text.TextUtils;
import android.util.Log;
import android.util.ArraySet;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.FastPrintWriter;
@@ -35,7 +35,6 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
@@ -324,8 +323,8 @@ public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query,
@SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
@TestApi
public IpcDataCache(int maxEntries, @NonNull @IpcDataCacheModule String module,
@NonNull String api,
@NonNull String cacheName, @NonNull QueryHandler<Query, Result> computer) {
@NonNull String api, @NonNull String cacheName,
@NonNull QueryHandler<Query, Result> computer) {
super(maxEntries, module, api, cacheName, computer);
}
@@ -382,4 +381,210 @@ public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query,
@NonNull String api) {
PropertyInvalidatedCache.invalidateCache(module, api);
}
/**
* This is a convenience class that encapsulates configuration information for a
* cache. It may be supplied to the cache constructors in lieu of the other
* parameters. The class captures maximum entry count, the module, the key, and the
* api.
*
* There are three specific use cases supported by this class.
*
* 1. Instance-per-cache: create a static instance of this class using the same
* parameters as would have been given to IpcDataCache (or
* PropertyInvalidatedCache). This static instance provides a hook for the
* invalidateCache() and disableForLocalProcess() calls, which, generally, must
* also be static.
*
* 2. Short-hand for shared configuration parameters: create an instance of this class
* to capture the maximum number of entries and the module to be used by more than
* one cache in the class. Refer to this instance when creating new configs. Only
* the api and (optionally key) for the new cache must be supplied.
*
* 3. Tied caches: create a static instance of this class to capture the maximum
* number of entries, the module, and the key. Refer to this instance when
* creating a new config that differs in only the api. The new config can be
* created as part of the cache constructor. All caches that trace back to the
* root config share the same key and are invalidated by the invalidateCache()
* method of the root config. All caches that trace back to the root config can be
* disabled in the local process by the disableAllForCurrentProcess() method of the
* root config.
*
* @hide
*/
public static class Config {
private final int mMaxEntries;
@IpcDataCacheModule
private final String mModule;
private final String mApi;
private final String mName;
/**
* The list of cache names that were created extending this Config. If
* disableForCurrentProcess() is invoked on this config then all children will be
* disabled. Furthermore, any new children based off of this config will be
* disabled. The construction order guarantees that new caches will be disabled
* before they are created (the Config must be created before the IpcDataCache is
* created).
*/
private ArraySet<String> mChildren;
/**
* True if registered children are disabled in the current process. If this is
* true then all new children are disabled as they are registered.
*/
private boolean mDisabled = false;
public Config(int maxEntries, @NonNull @IpcDataCacheModule String module,
@NonNull String api, @NonNull String name) {
mMaxEntries = maxEntries;
mModule = module;
mApi = api;
mName = name;
}
/**
* A short-hand constructor that makes the name the same as the api.
*/
public Config(int maxEntries, @NonNull @IpcDataCacheModule String module,
@NonNull String api) {
this(maxEntries, module, api, api);
}
/**
* Copy the module and max entries from the Config and take the api and name from
* the parameter list.
*/
public Config(@NonNull Config root, @NonNull String api, @NonNull String name) {
this(root.maxEntries(), root.module(), api, name);
}
/**
* Copy the module and max entries from the Config and take the api and name from
* the parameter list.
*/
public Config(@NonNull Config root, @NonNull String api) {
this(root.maxEntries(), root.module(), api, api);
}
/**
* Fetch a config that is a child of <this>. The child shares the same api as the
* parent and is registered with the parent for the purposes of disabling in the
* current process.
*/
public Config child(@NonNull String name) {
final Config result = new Config(this, api(), name);
registerChild(name);
return result;
}
public final int maxEntries() {
return mMaxEntries;
}
@IpcDataCacheModule
public final @NonNull String module() {
return mModule;
}
public final @NonNull String api() {
return mApi;
}
public final @NonNull String name() {
return mName;
}
/**
* Register a child cache name. If disableForCurrentProcess() has been called
* against this cache, disable th new child.
*/
private final void registerChild(String name) {
synchronized (this) {
if (mChildren == null) {
mChildren = new ArraySet<>();
}
mChildren.add(name);
if (mDisabled) {
IpcDataCache.disableForCurrentProcess(name);
}
}
}
/**
* Invalidate all caches that share this Config's module and api.
*/
public void invalidateCache() {
IpcDataCache.invalidateCache(mModule, mApi);
}
/**
* Disable all caches that share this Config's name.
*/
public void disableForCurrentProcess() {
IpcDataCache.disableForCurrentProcess(mName);
}
/**
* Disable this cache and all children. Any child that is added in the future
* will alwo be disabled.
*/
public void disableAllForCurrentProcess() {
synchronized (this) {
mDisabled = true;
disableForCurrentProcess();
if (mChildren != null) {
for (String c : mChildren) {
IpcDataCache.disableForCurrentProcess(c);
}
}
}
}
}
/**
* Create a new cache using a config.
* @hide
*/
public IpcDataCache(@NonNull Config config, @NonNull QueryHandler<Query, Result> computer) {
super(config.maxEntries(), config.module(), config.api(), config.name(), computer);
}
/**
* An interface suitable for a lambda expression instead of a QueryHandler.
* @hide
*/
public interface RemoteCall<Query, Result> {
Result apply(Query query) throws RemoteException;
}
/**
* This is a query handler that is created with a lambda expression that is invoked
* every time the handler is called. The handler is specifically meant for services
* hosted by system_server; the handler automatically rethrows RemoteException as a
* RuntimeException, which is the usual handling for failed binder calls.
*/
private static class SystemServerCallHandler<Query, Result>
extends IpcDataCache.QueryHandler<Query, Result> {
private final RemoteCall<Query, Result> mHandler;
public SystemServerCallHandler(RemoteCall handler) {
mHandler = handler;
}
@Override
public Result apply(Query query) {
try {
return mHandler.apply(query);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
/**
* Create a cache using a config and a lambda expression.
* @hide
*/
public IpcDataCache(@NonNull Config config, @NonNull RemoteCall<Query, Result> computer) {
this(config, new SystemServerCallHandler<>(computer));
}
}

View File

@@ -53,6 +53,14 @@ public class IpcDataCacheTest {
return value(x);
}
// A single query but this can throw an exception.
boolean query(int x, boolean y) throws RemoteException {
if (y) {
throw new RemoteException();
}
return query(x);
}
// Return the expected value of an input, without incrementing the query count.
boolean value(int x) {
return x % 3 == 0;
@@ -138,6 +146,47 @@ public class IpcDataCacheTest {
tester.verify(9);
}
// This test is disabled pending an sepolicy change that allows any app to set the
// test property.
@Test
public void testRemoteCall() {
// A stand-in for the binder. The test verifies that calls are passed through to
// this class properly.
ServerProxy tester = new ServerProxy();
// Create a cache that uses simple arithmetic to computer its values.
IpcDataCache.Config config = new IpcDataCache.Config(4, MODULE, API, "testCache2");
IpcDataCache<Integer, Boolean> testCache =
new IpcDataCache<>(config, (x) -> tester.query(x, x % 10 == 9));
IpcDataCache.setTestMode(true);
testCache.testPropertyName();
tester.verify(0);
assertEquals(tester.value(3), testCache.query(3));
tester.verify(1);
assertEquals(tester.value(3), testCache.query(3));
tester.verify(2);
testCache.invalidateCache();
assertEquals(tester.value(3), testCache.query(3));
tester.verify(3);
assertEquals(tester.value(5), testCache.query(5));
tester.verify(4);
assertEquals(tester.value(5), testCache.query(5));
tester.verify(4);
assertEquals(tester.value(3), testCache.query(3));
tester.verify(4);
try {
testCache.query(9);
assertEquals(false, true); // The code should not reach this point.
} catch (RuntimeException e) {
assertEquals(e.getCause() instanceof RemoteException, true);
}
tester.verify(4);
}
@Test
public void testDisableCache() {
@@ -225,6 +274,17 @@ public class IpcDataCacheTest {
testPropertyName();
}
TestCache(IpcDataCache.Config c) {
this(c, new TestQuery());
}
TestCache(IpcDataCache.Config c, TestQuery query) {
super(c, query);
mQuery = query;
setTestMode(true);
testPropertyName();
}
int getRecomputeCount() {
return mQuery.getRecomputeCount();
}
@@ -309,4 +369,48 @@ public class IpcDataCacheTest {
assertEquals("foo5", cache.query(5));
assertEquals(3, cache.getRecomputeCount());
}
@Test
public void testConfig() {
IpcDataCache.Config a = new IpcDataCache.Config(8, MODULE, "apiA");
TestCache ac = new TestCache(a);
assertEquals(8, a.maxEntries());
assertEquals(MODULE, a.module());
assertEquals("apiA", a.api());
assertEquals("apiA", a.name());
IpcDataCache.Config b = new IpcDataCache.Config(a, "apiB");
TestCache bc = new TestCache(b);
assertEquals(8, b.maxEntries());
assertEquals(MODULE, b.module());
assertEquals("apiB", b.api());
assertEquals("apiB", b.name());
IpcDataCache.Config c = new IpcDataCache.Config(a, "apiC", "nameC");
TestCache cc = new TestCache(c);
assertEquals(8, c.maxEntries());
assertEquals(MODULE, c.module());
assertEquals("apiC", c.api());
assertEquals("nameC", c.name());
IpcDataCache.Config d = a.child("nameD");
TestCache dc = new TestCache(d);
assertEquals(8, d.maxEntries());
assertEquals(MODULE, d.module());
assertEquals("apiA", d.api());
assertEquals("nameD", d.name());
a.disableForCurrentProcess();
assertEquals(ac.isDisabled(), true);
assertEquals(bc.isDisabled(), false);
assertEquals(cc.isDisabled(), false);
assertEquals(dc.isDisabled(), false);
a.disableAllForCurrentProcess();
assertEquals(ac.isDisabled(), true);
assertEquals(bc.isDisabled(), false);
assertEquals(cc.isDisabled(), false);
assertEquals(dc.isDisabled(), true);
IpcDataCache.Config e = a.child("nameE");
TestCache ec = new TestCache(e);
assertEquals(ec.isDisabled(), true);
}
}

View File

@@ -1781,7 +1781,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@VisibleForTesting
DevicePolicyManagerService(Injector injector) {
DevicePolicyManager.disableGetKeyguardDisabledFeaturesCache();
DevicePolicyManager.disableLocalCaches();
mInjector = injector;
mContext = Objects.requireNonNull(injector.mContext);