Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9c0907c3 authored by Hai Zhang's avatar Hai Zhang
Browse files

Move default app related methods to role manager.

Default apps (browser, dialer and home) are by no means a concept of
permission, so they should not be exposed as permission
manager API. Instead, they should be accessed via role manager API.

This change moves getDefaultBrowser() and setDefaultBrowser()'s AIDL
interface and implementation into RoleManagerService. Package manager
has a number of special behaviors regarding these default apps, so we
can not pretend that package manager doesn't know about these
roles. After all, we can say permission and role are at the same level
in the system after recent refactoring that split permission and
package. So package manager is reusing the public role API now.

The new methods moved to RoleManagerService needs to be system APIs on
RoleManager, because IRoleManager cannot be a system API and we need
to delegate method calls to it from ApplicationPackageManager.

The other methods directly calling into DefaultPermissionGrantPolicy
should be moved/refactored as well, depending on whether we want to
mainline it, in a later change.

Bug: 158736025
Test: presubmit
Change-Id: I84b9519a084e410875a3c3e88b33e9a612e7de98
parent c4679fb3
Loading
Loading
Loading
Loading
+5 −10
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import android.annotation.Nullable;
import android.annotation.StringRes;
import android.annotation.UserIdInt;
import android.annotation.XmlRes;
import android.app.role.RoleManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -2306,20 +2307,14 @@ public class ApplicationPackageManager extends PackageManager {

    @Override
    public String getDefaultBrowserPackageNameAsUser(int userId) {
        try {
            return mPermissionManager.getDefaultBrowser(userId);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
        RoleManager roleManager = mContext.getSystemService(RoleManager.class);
        return roleManager.getBrowserRoleHolder(userId);
    }

    @Override
    public boolean setDefaultBrowserPackageNameAsUser(String packageName, int userId) {
        try {
            return mPermissionManager.setDefaultBrowser(packageName, userId);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
        RoleManager roleManager = mContext.getSystemService(RoleManager.class);
        return roleManager.setBrowserRoleHolder(packageName, userId);
    }

    @Override
+5 −1
Original line number Diff line number Diff line
@@ -53,5 +53,9 @@ interface IRoleManager {

    List<String> getHeldRolesFromController(in String packageName);

    String getDefaultSmsPackage(int userId);
    String getBrowserRoleHolder(int userId);

    boolean setBrowserRoleHolder(String packageName, int userId);

    String getSmsRoleHolder(int userId);
}
+50 −6
Original line number Diff line number Diff line
@@ -613,12 +613,56 @@ public final class RoleManager {
    }

    /**
     * Allows getting the role holder for {@link #ROLE_SMS} without
     * {@link Manifest.permission#OBSERVE_ROLE_HOLDERS}, as required by
     * {@link android.provider.Telephony.Sms#getDefaultSmsPackage(Context)}
     * Get the role holder of {@link #ROLE_BROWSER} without requiring
     * {@link Manifest.permission#OBSERVE_ROLE_HOLDERS}, as in
     * {@link android.content.pm.PackageManager#getDefaultBrowserPackageNameAsUser(int)}
     *
     * @param userId the user ID
     * @return the package name of the default browser, or {@code null} if none
     *
     * @hide
     */
    @Nullable
    //@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
    public String getBrowserRoleHolder(@UserIdInt int userId) {
        try {
            return mService.getBrowserRoleHolder(userId);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Set the role holder of {@link #ROLE_BROWSER} requiring
     * {@link Manifest.permission.SET_PREFERRED_APPLICATIONS} instead of
     * {@link Manifest.permission#MANAGE_ROLE_HOLDERS}, as in
     * {@link android.content.pm.PackageManager#setDefaultBrowserPackageNameAsUser(String, int)}
     *
     * @param packageName the package name of the default browser, or {@code null} if none
     * @param userId the user ID
     * @return whether the default browser was set successfully
     *
     * @hide
     */
    @Nullable
    @RequiresPermission(Manifest.permission.SET_PREFERRED_APPLICATIONS)
    //@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
    public boolean setBrowserRoleHolder(@Nullable String packageName, @UserIdInt int userId) {
        try {
            return mService.setBrowserRoleHolder(packageName, userId);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Allows getting the role holder for {@link #ROLE_SMS} without requiring
     * {@link Manifest.permission#OBSERVE_ROLE_HOLDERS}, as in
     * {@link android.provider.Telephony.Sms#getDefaultSmsPackage(Context)}.
     *
     * @param userId the user ID to get the default SMS package for
     * @return the package name of the default SMS app, or {@code null} if none
     *
     * @param userId The user ID to get the default SMS package for.
     * @return the package name of the default SMS app, or {@code null} if not configured.
     * @hide
     */
    @Nullable
@@ -626,7 +670,7 @@ public final class RoleManager {
    @TestApi
    public String getSmsRoleHolder(@UserIdInt int userId) {
        try {
            return mService.getDefaultSmsPackage(userId);
            return mService.getSmsRoleHolder(userId);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
+0 −4
Original line number Diff line number Diff line
@@ -73,10 +73,6 @@ interface IPermissionManager {

    void resetRuntimePermissions();

    boolean setDefaultBrowser(String packageName, int userId);

    String getDefaultBrowser(int userId);

    void grantDefaultPermissionsToEnabledCarrierApps(in String[] packageNames, int userId);

    void grantDefaultPermissionsToEnabledImsServices(in String[] packageNames, int userId);
+187 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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 com.android.server.pm;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.role.RoleManager;
import android.os.Binder;
import android.os.UserHandle;
import android.util.Slog;

import com.android.internal.infra.AndroidFuture;
import com.android.internal.util.CollectionUtils;
import com.android.server.FgThread;
import com.android.server.pm.permission.PermissionManagerServiceInternal;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
import java.util.function.Supplier;

/**
 * Interacts with {@link RoleManager} to provide and manage default apps.
 */
public class DefaultAppProvider {
    @NonNull
    private final Supplier<RoleManager> mRoleManagerSupplier;
    @NonNull
    private final PermissionManagerServiceInternal mPermissionManager;

    /**
     * Create a new instance of this class
     *
     * @param roleManagerSupplier the supplier for {@link RoleManager}
     * @param permissionManager the {@link PermissionManagerServiceInternal}
     */
    public DefaultAppProvider(@NonNull Supplier<RoleManager> roleManagerSupplier, @NonNull
            PermissionManagerServiceInternal permissionManager) {
        mRoleManagerSupplier = roleManagerSupplier;
        mPermissionManager = permissionManager;
    }

    /**
     * Get the package name of the default browser.
     *
     * @param userId the user ID
     * @return the package name of the default browser, or {@code null} if none
     */
    @Nullable
    public String getDefaultBrowser(@UserIdInt int userId) {
        return getRoleHolder(RoleManager.ROLE_BROWSER, userId);
    }

    /**
     * Set the package name of the default browser.
     *
     * @param packageName package name of the default browser, or {@code null} to unset
     * @param async whether the operation should be asynchronous
     * @param doGrant whether to grant default permissions
     * @param userId the user ID
     * @return whether the default browser was successfully set.
     */
    public boolean setDefaultBrowser(@Nullable String packageName, boolean async, boolean doGrant,
            @UserIdInt int userId) {
        if (userId == UserHandle.USER_ALL) {
            return false;
        }
        final RoleManager roleManager = mRoleManagerSupplier.get();
        if (roleManager == null) {
            return false;
        }
        final UserHandle user = UserHandle.of(userId);
        final Executor executor = FgThread.getExecutor();
        final AndroidFuture<Void> future = new AndroidFuture<>();
        final Consumer<Boolean> callback = successful -> {
            if (successful) {
                future.complete(null);
            } else {
                future.completeExceptionally(new RuntimeException());
            }
        };
        final long identity = Binder.clearCallingIdentity();
        try {
            if (packageName != null) {
                roleManager.addRoleHolderAsUser(RoleManager.ROLE_BROWSER, packageName, 0, user,
                        executor, callback);
            } else {
                roleManager.clearRoleHoldersAsUser(RoleManager.ROLE_BROWSER, 0, user, executor,
                        callback);
            }
            if (!async) {
                try {
                    future.get(5, TimeUnit.SECONDS);
                } catch (InterruptedException | ExecutionException | TimeoutException e) {
                    Slog.e(PackageManagerService.TAG, "Exception while setting default browser: "
                            + packageName, e);
                    return false;
                }
            }
            if (doGrant && packageName != null) {
                mPermissionManager.grantDefaultPermissionsToDefaultBrowser(packageName, userId);
            }
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
        return true;
    }

    /**
     * Get the package name of the default dialer.
     *
     * @param userId the user ID
     * @return the package name of the default dialer, or {@code null} if none
     */
    @Nullable
    public String getDefaultDialer(@NonNull int userId) {
        return getRoleHolder(RoleManager.ROLE_DIALER, userId);
    }

    /**
     * Get the package name of the default home.
     *
     * @param userId the user ID
     * @return the package name of the default home, or {@code null} if none
     */
    @Nullable
    public String getDefaultHome(@NonNull int userId) {
        return getRoleHolder(RoleManager.ROLE_HOME, userId);
    }

    /**
     * Set the package name of the default home.
     *
     * @param packageName package name of the default home
     * @param userId the user ID
     * @param executor the {@link Executor} to execute callback on
     * @param callback the callback made after the default home as been updated
     * @return whether the default home was set
     */
    public boolean setDefaultHome(@NonNull String packageName, @UserIdInt int userId,
            @NonNull Executor executor, @NonNull Consumer<Boolean> callback) {
        final RoleManager roleManager = mRoleManagerSupplier.get();
        if (roleManager == null) {
            return false;
        }
        final long identity = Binder.clearCallingIdentity();
        try {
            roleManager.addRoleHolderAsUser(RoleManager.ROLE_HOME, packageName, 0,
                    UserHandle.of(userId), executor, callback);
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
        return true;
    }

    @Nullable
    private String getRoleHolder(@NonNull String roleName, @NonNull int userId) {
        final RoleManager roleManager = mRoleManagerSupplier.get();
        if (roleManager == null) {
            return null;
        }
        final long identity = Binder.clearCallingIdentity();
        try {
            return CollectionUtils.firstOrNull(roleManager.getRoleHoldersAsUser(roleName,
                    UserHandle.of(userId)));
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
    }
}
Loading