blob: 2bdca7d87a78f0e6a107e9003686b270dc29b981 [file] [log] [blame]
/*
* 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.content.om;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.compat.Compatibility;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
import android.content.Context;
import android.os.Build;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import com.android.server.SystemConfig;
import java.util.List;
/**
* Updates OverlayManager state; gets information about installed overlay packages.
*
* <p>Users of this API must be actors of any overlays they desire to change the state of.</p>
*
* <p>An actor is a package responsible for managing the state of overlays targeting overlayables
* that specify the actor. For example, an actor may enable or disable an overlay or otherwise
* change its state.</p>
*
* <p>Actors are specified as part of the overlayable definition.
*
* <pre>{@code
* <overlayable name="OverlayableResourcesName" actor="overlay://namespace/actorName">
* }</pre></p>
*
* <p>Actors are defined through {@link SystemConfig}. Only system packages can be used.
* The namespace "android" is reserved for use by AOSP and any "android" definitions must
* have an implementation on device that fulfill their intended functionality.</p>
*
* <pre>{@code
* <named-actor
* namespace="namespace"
* name="actorName"
* package="com.example.pkg"
* />
* }</pre></p>
*
* <p>An actor can manipulate a particular overlay if any of the following is true:
* <ul>
* <li>its UID is {@link Process#ROOT_UID}, {@link Process#SYSTEM_UID}</li>
* <li>it is the target of the overlay package</li>
* <li>it has the CHANGE_OVERLAY_PACKAGES permission and the target does not specify an actor</li>
* <li>it is the actor specified by the overlayable</li>
* </ul></p>
*
* @hide
*/
@SystemApi
@SystemService(Context.OVERLAY_SERVICE)
public class OverlayManager {
private final IOverlayManager mService;
private final Context mContext;
/**
* Pre R a {@link java.lang.SecurityException} would only be thrown by setEnabled APIs (e
* .g. {@link #setEnabled(String, boolean, UserHandle)}) for a permission error.
* Since R this no longer holds true, and {@link java.lang.SecurityException} can be
* thrown for any number of reasons, none of which are exposed to the caller.
*
* <p>To maintain existing API behavior, if a legacy permission failure or actor enforcement
* failure occurs for an app not yet targeting R, coerce it into an {@link
* java.lang.IllegalStateException}, which existed in the source prior to R.
*/
@ChangeId
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
private static final long THROW_SECURITY_EXCEPTIONS = 147340954;
/**
* Creates a new instance.
*
* @param context The current context in which to operate.
* @param service The backing system service.
*
* @hide
*/
public OverlayManager(Context context, IOverlayManager service) {
mContext = context;
mService = service;
}
/** @hide */
public OverlayManager(Context context) {
this(context, IOverlayManager.Stub.asInterface(
ServiceManager.getService(Context.OVERLAY_SERVICE)));
}
/**
* Request that an overlay package is enabled and any other overlay packages with the same
* target package and category are disabled.
*
* If a set of overlay packages share the same category, single call to this method is
* equivalent to multiple calls to {@link #setEnabled(String, boolean, UserHandle)}.
*
* The caller must pass the actor requirements specified in the class comment.
*
* @param packageName the name of the overlay package to enable.
* @param user The user for which to change the overlay.
*
* @throws SecurityException when caller is not allowed to enable {@param packageName}
* @throws IllegalStateException when enabling fails otherwise
*
* @hide
*/
@SystemApi
@RequiresPermission(anyOf = {
"android.permission.INTERACT_ACROSS_USERS",
"android.permission.INTERACT_ACROSS_USERS_FULL"
})
public void setEnabledExclusiveInCategory(@NonNull final String packageName,
@NonNull UserHandle user) throws SecurityException, IllegalStateException {
try {
if (!mService.setEnabledExclusiveInCategory(packageName, user.getIdentifier())) {
throw new IllegalStateException("setEnabledExclusiveInCategory failed");
}
} catch (SecurityException e) {
rethrowSecurityException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Request that an overlay package is enabled or disabled.
*
* While {@link #setEnabledExclusiveInCategory(String, UserHandle)} doesn't support disabling
* every overlay in a category, this method allows you to disable everything.
*
* The caller must pass the actor requirements specified in the class comment.
*
* @param packageName the name of the overlay package to enable.
* @param enable {@code false} if the overlay should be turned off.
* @param user The user for which to change the overlay.
*
* @throws SecurityException when caller is not allowed to enable/disable {@param packageName}
* @throws IllegalStateException when enabling/disabling fails otherwise
*
* @hide
*/
@SystemApi
@RequiresPermission(anyOf = {
"android.permission.INTERACT_ACROSS_USERS",
"android.permission.INTERACT_ACROSS_USERS_FULL"
})
public void setEnabled(@NonNull final String packageName, final boolean enable,
@NonNull UserHandle user) throws SecurityException, IllegalStateException {
try {
if (!mService.setEnabled(packageName, enable, user.getIdentifier())) {
throw new IllegalStateException("setEnabled failed");
}
} catch (SecurityException e) {
rethrowSecurityException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Returns information about the overlay with the given package name for
* the specified user.
*
* @param packageName The name of the package.
* @param userHandle The user to get the OverlayInfos for.
* @return An OverlayInfo object; if no overlays exist with the
* requested package name, null is returned.
*
* @hide
*/
@SystemApi
@Nullable
public OverlayInfo getOverlayInfo(@NonNull final String packageName,
@NonNull final UserHandle userHandle) {
try {
return mService.getOverlayInfo(packageName, userHandle.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Returns information about all overlays for the given target package for
* the specified user. The returned list is ordered according to the
* overlay priority with the highest priority at the end of the list.
*
* @param targetPackageName The name of the target package.
* @param user The user to get the OverlayInfos for.
* @return A list of OverlayInfo objects; if no overlays exist for the
* requested package, an empty list is returned.
*
* @hide
*/
@SystemApi
@RequiresPermission(anyOf = {
"android.permission.INTERACT_ACROSS_USERS",
"android.permission.INTERACT_ACROSS_USERS_FULL"
})
@NonNull
public List<OverlayInfo> getOverlayInfosForTarget(@NonNull final String targetPackageName,
@NonNull UserHandle user) {
try {
return mService.getOverlayInfosForTarget(targetPackageName, user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Clear part of the overlay manager's internal cache of PackageInfo
* objects. Only intended for testing.
*
* @param targetPackageName The name of the target package.
* @param user The user to get the OverlayInfos for.
*
* @hide
*/
@TestApi
@RequiresPermission(anyOf = {
"android.permission.INTERACT_ACROSS_USERS",
})
@NonNull
public void invalidateCachesForOverlay(@NonNull final String targetPackageName,
@NonNull UserHandle user) {
try {
mService.invalidateCachesForOverlay(targetPackageName, user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Starting on R, actor enforcement and app visibility changes introduce additional failure
* cases, but the SecurityException thrown with these checks is unexpected for existing
* consumers of the API.
*
* The only prior case it would be thrown is with a permission failure, but the calling
* application would be able to verify that themselves, and so they may choose to ignore
* catching SecurityException when calling these APIs.
*
* For R, this no longer holds true, and SecurityExceptions can be thrown for any number of
* reasons, none of which are exposed to the caller. So for consumers targeting below R,
* transform these SecurityExceptions into IllegalStateExceptions, which are a little more
* expected to be thrown by the setEnabled APIs.
*
* This will mask the prior permission exception if it applies, but it's assumed that apps
* wouldn't call the APIs without the permission on prior versions, and so it's safe to ignore.
*/
private void rethrowSecurityException(SecurityException e) {
if (!Compatibility.isChangeEnabled(THROW_SECURITY_EXCEPTIONS)) {
throw new IllegalStateException(e);
} else {
throw e;
}
}
}