/* * 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.admin; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.content.Context; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.os.RemoteException; import android.provider.DeviceConfig; import android.util.DisplayMetrics; import android.util.Log; import java.util.ArrayList; import java.util.Objects; import java.util.Set; import java.util.function.Supplier; /** * Class containing required APIs to set, reset, and get device policy related resources. */ public class DevicePolicyResourcesManager { private static String TAG = "DevicePolicyResourcesManager"; private static String DISABLE_RESOURCES_UPDATABILITY_FLAG = "disable_resources_updatability"; private static boolean DEFAULT_DISABLE_RESOURCES_UPDATABILITY = false; private final Context mContext; private final IDevicePolicyManager mService; /** * @hide */ protected DevicePolicyResourcesManager(Context context, IDevicePolicyManager service) { mContext = context; mService = service; } /** * For each {@link DevicePolicyDrawableResource} item in {@code drawables}, if * {@link DevicePolicyDrawableResource#getDrawableSource()} is not set, it updates the drawable * resource for the combination of {@link DevicePolicyDrawableResource#getDrawableId()} and * {@link DevicePolicyDrawableResource#getDrawableStyle()} to the drawable with resource ID * {@link DevicePolicyDrawableResource#getResourceIdInCallingPackage()}, * meaning any system UI surface calling {@link #getDrawable} with {@code drawableId} and * {@code drawableStyle} will get the new resource after this API is called. * *

Otherwise, if {@link DevicePolicyDrawableResource#getDrawableSource()} is set, it * overrides any drawables that was set for the same {@code drawableId} and * {@code drawableStyle} for the provided source. * *

Sends a broadcast with action * {@link DevicePolicyManager#ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to registered receivers * when a resource has been updated successfully. * *

Important notes to consider when using this API: *

* * @param drawables The list of {@link DevicePolicyDrawableResource} to update. * * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES) public void setDrawables(@NonNull Set drawables) { if (mService != null) { try { mService.setDrawables(new ArrayList<>(drawables)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } } /** * Removes all updated drawables for the list of {@code drawableIds} that was previously set by * calling {@link #setDrawables}, meaning any subsequent calls to {@link #getDrawable} for the * provided IDs with any {@code drawableStyle} and any {@code drawableSource} will return the * default drawable from {@code defaultDrawableLoader}. * *

Sends a broadcast with action * {@link DevicePolicyManager#ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to registered receivers * when a resource has been reset successfully. * * @param drawableIds The list of IDs to remove. * * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES) public void resetDrawables(@NonNull Set drawableIds) { if (mService != null) { try { mService.resetDrawables(new ArrayList<>(drawableIds)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } } /** * Returns the appropriate updated drawable for the {@code drawableId} with style * {@code drawableStyle} if one was set using {@code setDrawables}, otherwise returns the * drawable from {@code defaultDrawableLoader}. * *

Also returns the drawable from {@code defaultDrawableLoader} if {@code drawableId} * is {@link DevicePolicyResources#UNDEFINED}. * *

Calls to this API will not return {@code null} unless no updated drawable was found * and the call to {@code defaultDrawableLoader} returned {@code null}. * *

This API uses the screen density returned from {@link Resources#getConfiguration()}, to * set a different value use * {@link #getDrawableForDensity(String, String, int, Supplier)}. * *

Callers should register for * {@link DevicePolicyManager#ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to get notified when a * resource has been updated. * *

Note that each call to this API loads the resource from the package that called * {@code setDrawables} to set the updated resource. * * @param drawableId The drawable ID to get the updated resource for. * @param drawableStyle The drawable style to use. * @param defaultDrawableLoader To get the default drawable if no updated drawable was set for * the provided params. */ @Nullable public Drawable getDrawable( @NonNull String drawableId, @NonNull String drawableStyle, @NonNull Supplier defaultDrawableLoader) { return getDrawable( drawableId, drawableStyle, DevicePolicyResources.UNDEFINED, defaultDrawableLoader); } /** * Similar to {@link #getDrawable(String, String, Supplier)}, but also accepts * a {@code drawableSource} which could result in returning a different drawable than * {@link #getDrawable(String, String, Supplier)} if an override was set for that specific * source. * *

If {@code drawableSource} is {@link DevicePolicyResources#UNDEFINED}, it returns the * appropriate string for {@code drawableId} and {@code drawableStyle} similar to * {@link #getDrawable(String, String, Supplier)}. * *

Calls to this API will not return {@code null} unless no updated drawable was found * and the call to {@code defaultDrawableLoader} returned {@code null}. * *

Callers should register for * {@link DevicePolicyManager#ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to get notified when a * resource has been updated. * * @param drawableId The drawable ID to get the updated resource for. * @param drawableStyle The drawable style to use. * @param drawableSource The source for the caller. * @param defaultDrawableLoader To get the default drawable if no updated drawable was set for * the provided params. */ @Nullable public Drawable getDrawable( @NonNull String drawableId, @NonNull String drawableStyle, @NonNull String drawableSource, @NonNull Supplier defaultDrawableLoader) { Objects.requireNonNull(drawableId, "drawableId can't be null"); Objects.requireNonNull(drawableStyle, "drawableStyle can't be null"); Objects.requireNonNull(drawableSource, "drawableSource can't be null"); Objects.requireNonNull(defaultDrawableLoader, "defaultDrawableLoader can't be null"); if (drawableId.equals(DevicePolicyResources.UNDEFINED) || DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER, DISABLE_RESOURCES_UPDATABILITY_FLAG, DEFAULT_DISABLE_RESOURCES_UPDATABILITY)) { return ParcelableResource.loadDefaultDrawable(defaultDrawableLoader); } if (mService != null) { try { ParcelableResource resource = mService.getDrawable( drawableId, drawableStyle, drawableSource); if (resource == null) { return ParcelableResource.loadDefaultDrawable(defaultDrawableLoader); } return resource.getDrawable( mContext, /* density= */ 0, defaultDrawableLoader); } catch (RemoteException e) { Log.e( TAG, "Error getting the updated drawable from DevicePolicyManagerService.", e); return ParcelableResource.loadDefaultDrawable(defaultDrawableLoader); } } return ParcelableResource.loadDefaultDrawable(defaultDrawableLoader); } /** * Similar to {@link #getDrawable(String, String, Supplier)}, but also accepts * {@code density}. See {@link Resources#getDrawableForDensity(int, int, Resources.Theme)}. * *

Calls to this API will not return {@code null} unless no updated drawable was found * and the call to {@code defaultDrawableLoader} returned {@code null}. * *

Callers should register for * {@link DevicePolicyManager#ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to get notified when a * resource has been updated. * * @param drawableId The drawable ID to get the updated resource for. * @param drawableStyle The drawable style to use. * @param density The desired screen density indicated by the resource as * found in {@link DisplayMetrics}. A value of 0 means to use the * density returned from {@link Resources#getConfiguration()}. * @param defaultDrawableLoader To get the default drawable if no updated drawable was set for * the provided params. */ @Nullable public Drawable getDrawableForDensity( @NonNull String drawableId, @NonNull String drawableStyle, int density, @NonNull Supplier defaultDrawableLoader) { return getDrawableForDensity( drawableId, drawableStyle, DevicePolicyResources.UNDEFINED, density, defaultDrawableLoader); } /** * Similar to {@link #getDrawable(String, String, String, Supplier)}, but also accepts * {@code density}. See {@link Resources#getDrawableForDensity(int, int, Resources.Theme)}. * *

Calls to this API will not return {@code null} unless no updated drawable was found * and the call to {@code defaultDrawableLoader} returned {@code null}. * *

Callers should register for * {@link DevicePolicyManager#ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to get notified when a * resource has been updated. * * @param drawableId The drawable ID to get the updated resource for. * @param drawableStyle The drawable style to use. * @param drawableSource The source for the caller. * @param density The desired screen density indicated by the resource as * found in {@link DisplayMetrics}. A value of 0 means to use the * density returned from {@link Resources#getConfiguration()}. * @param defaultDrawableLoader To get the default drawable if no updated drawable was set for * the provided params. */ @Nullable public Drawable getDrawableForDensity( @NonNull String drawableId, @NonNull String drawableStyle, @NonNull String drawableSource, int density, @NonNull Supplier defaultDrawableLoader) { Objects.requireNonNull(drawableId, "drawableId can't be null"); Objects.requireNonNull(drawableStyle, "drawableStyle can't be null"); Objects.requireNonNull(drawableSource, "drawableSource can't be null"); Objects.requireNonNull(defaultDrawableLoader, "defaultDrawableLoader can't be null"); if (drawableId.equals(DevicePolicyResources.UNDEFINED) || DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER, DISABLE_RESOURCES_UPDATABILITY_FLAG, DEFAULT_DISABLE_RESOURCES_UPDATABILITY)) { return ParcelableResource.loadDefaultDrawable(defaultDrawableLoader); } if (mService != null) { try { ParcelableResource resource = mService.getDrawable( drawableId, drawableStyle, drawableSource); if (resource == null) { return ParcelableResource.loadDefaultDrawable(defaultDrawableLoader); } return resource.getDrawable(mContext, density, defaultDrawableLoader); } catch (RemoteException e) { Log.e( TAG, "Error getting the updated drawable from DevicePolicyManagerService.", e); return ParcelableResource.loadDefaultDrawable(defaultDrawableLoader); } } return ParcelableResource.loadDefaultDrawable(defaultDrawableLoader); } /** * Similar to {@link #getDrawable(String, String, String, Supplier)} but returns an * {@link Icon} instead of a {@link Drawable}. * * @param drawableId The drawable ID to get the updated resource for. * @param drawableStyle The drawable style to use. * @param drawableSource The source for the caller. * @param defaultIcon Returned if no updated drawable was set for the provided params. */ @Nullable public Icon getDrawableAsIcon( @NonNull String drawableId, @NonNull String drawableStyle, @NonNull String drawableSource, @Nullable Icon defaultIcon) { Objects.requireNonNull(drawableId, "drawableId can't be null"); Objects.requireNonNull(drawableStyle, "drawableStyle can't be null"); Objects.requireNonNull(drawableSource, "drawableSource can't be null"); Objects.requireNonNull(defaultIcon, "defaultIcon can't be null"); if (drawableId.equals(DevicePolicyResources.UNDEFINED) || DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER, DISABLE_RESOURCES_UPDATABILITY_FLAG, DEFAULT_DISABLE_RESOURCES_UPDATABILITY)) { return defaultIcon; } if (mService != null) { try { ParcelableResource resource = mService.getDrawable( drawableId, drawableStyle, drawableSource); if (resource == null) { return defaultIcon; } return Icon.createWithResource(resource.getPackageName(), resource.getResourceId()); } catch (RemoteException e) { Log.e( TAG, "Error getting the updated drawable from DevicePolicyManagerService.", e); return defaultIcon; } } return defaultIcon; } /** * Similar to {@link #getDrawable(String, String, Supplier)} but returns an {@link Icon} * instead of a {@link Drawable}. * * @param drawableId The drawable ID to get the updated resource for. * @param drawableStyle The drawable style to use. * @param defaultIcon Returned if no updated drawable was set for the provided params. */ @Nullable public Icon getDrawableAsIcon( @NonNull String drawableId, @NonNull String drawableStyle, @Nullable Icon defaultIcon) { return getDrawableAsIcon( drawableId, drawableStyle, DevicePolicyResources.UNDEFINED, defaultIcon); } /** * For each {@link DevicePolicyStringResource} item in {@code strings}, it updates the string * resource for {@link DevicePolicyStringResource#getStringId()} to the string with ID * {@code callingPackageResourceId}, meaning any system UI surface calling {@link #getString} * with {@code stringId} will get the new resource after this API is called. * *

Sends a broadcast with action * {@link DevicePolicyManager#ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to registered receivers * when a resource has been updated successfully. * *

Important notes to consider when using this API: *

* * @param strings The list of {@link DevicePolicyStringResource} to update. * * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES) public void setStrings(@NonNull Set strings) { if (mService != null) { try { mService.setStrings(new ArrayList<>(strings)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } } /** * Removes the updated strings for the list of {@code stringIds} that was previously set by * calling {@link #setStrings}, meaning any subsequent calls to {@link #getString} for the * provided IDs will return the default string from {@code defaultStringLoader}. * *

Sends a broadcast with action * {@link DevicePolicyManager#ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to registered receivers * when a resource has been reset successfully. * * @param stringIds The list of IDs to remove the updated resources for. * * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES) public void resetStrings(@NonNull Set stringIds) { if (mService != null) { try { mService.resetStrings(new ArrayList<>(stringIds)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } } /** * Returns the appropriate updated string for the {@code stringId} (see * {@code DevicePolicyResources.Strings}) if one was set using * {@code setStrings}, otherwise returns the string from {@code defaultStringLoader}. * *

Also returns the string from {@code defaultStringLoader} if {@code stringId} is * {@link DevicePolicyResources#UNDEFINED}. * *

Calls to this API will not return {@code null} unless no updated drawable was found * and the call to {@code defaultStringLoader} returned {@code null}. * *

Callers should register for * {@link DevicePolicyManager#ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to get notified when a * resource has been updated. * *

Note that each call to this API loads the resource from the package that called * {@code setStrings} to set the updated resource. * * @param stringId The IDs to get the updated resource for. * @param defaultStringLoader To get the default string if no updated string was set for * {@code stringId}. */ @Nullable public String getString( @NonNull String stringId, @NonNull Supplier defaultStringLoader) { Objects.requireNonNull(stringId, "stringId can't be null"); Objects.requireNonNull(defaultStringLoader, "defaultStringLoader can't be null"); if (stringId.equals(DevicePolicyResources.UNDEFINED) || DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER, DISABLE_RESOURCES_UPDATABILITY_FLAG, DEFAULT_DISABLE_RESOURCES_UPDATABILITY)) { return ParcelableResource.loadDefaultString(defaultStringLoader); } if (mService != null) { try { ParcelableResource resource = mService.getString(stringId); if (resource == null) { return ParcelableResource.loadDefaultString(defaultStringLoader); } return resource.getString(mContext, defaultStringLoader); } catch (RemoteException e) { Log.e( TAG, "Error getting the updated string from DevicePolicyManagerService.", e); return ParcelableResource.loadDefaultString(defaultStringLoader); } } return ParcelableResource.loadDefaultString(defaultStringLoader); } /** * Similar to {@link #getString(String, Supplier)} but accepts {@code formatArgs} and returns a * localized formatted string, substituting the format arguments as defined in * {@link java.util.Formatter} and {@link java.lang.String#format}, (see * {@link Resources#getString(int, Object...)}). * *

Calls to this API will not return {@code null} unless no updated drawable was found * and the call to {@code defaultStringLoader} returned {@code null}. * * @param stringId The IDs to get the updated resource for. * @param defaultStringLoader To get the default string if no updated string was set for * {@code stringId}. * @param formatArgs The format arguments that will be used for substitution. */ @Nullable @SuppressLint("SamShouldBeLast") public String getString( @NonNull String stringId, @NonNull Supplier defaultStringLoader, @NonNull Object... formatArgs) { Objects.requireNonNull(stringId, "stringId can't be null"); Objects.requireNonNull(defaultStringLoader, "defaultStringLoader can't be null"); if (stringId.equals(DevicePolicyResources.UNDEFINED) || DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER, DISABLE_RESOURCES_UPDATABILITY_FLAG, DEFAULT_DISABLE_RESOURCES_UPDATABILITY)) { return ParcelableResource.loadDefaultString(defaultStringLoader); } if (mService != null) { try { ParcelableResource resource = mService.getString(stringId); if (resource == null) { return ParcelableResource.loadDefaultString(defaultStringLoader); } return resource.getString(mContext, defaultStringLoader, formatArgs); } catch (RemoteException e) { Log.e( TAG, "Error getting the updated string from DevicePolicyManagerService.", e); return ParcelableResource.loadDefaultString(defaultStringLoader); } } return ParcelableResource.loadDefaultString(defaultStringLoader); } }