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

Commit 45a6e0e8 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Use a single interface for reading pre-Q and pre-S role state."

parents 79087bc7 ce2a8043
Loading
Loading
Loading
Loading
+0 −171
Original line number Diff line number Diff line
/*
 * 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 com.android.server.policy.role;

import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.role.RoleManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Slog;

import com.android.internal.R;
import com.android.internal.telephony.SmsApplication;
import com.android.internal.util.CollectionUtils;
import com.android.server.LocalServices;
import com.android.server.role.LegacyRoleHolderProvider;
import com.android.server.role.RoleManagerService;

import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
 * Logic to retrieve the various legacy(pre-Q) equivalents of role holders.
 *
 * Unlike {@link RoleManagerService} this is meant to be pretty high-level to allow for depending
 * on all kinds of various systems that are historically involved in legacy role resolution,
 * e.g. {@link SmsApplication}
 *
 * @see RoleManagerService#migrateRoleIfNecessary
 */
public class LegacyRoleResolutionPolicy implements LegacyRoleHolderProvider {

    private static final boolean DEBUG = false;
    private static final String LOG_TAG = "LegacyRoleResolutionPol";

    @NonNull
    private final Context mContext;

    public LegacyRoleResolutionPolicy(@NonNull Context context) {
        mContext = context;
    }

    @NonNull
    @Override
    public List<String> getLegacyRoleHolders(@NonNull String roleName, @UserIdInt int userId) {
        switch (roleName) {
            case RoleManager.ROLE_ASSISTANT: {
                String packageName;
                String setting = Settings.Secure.getStringForUser(mContext.getContentResolver(),
                        Settings.Secure.ASSISTANT, userId);
                // AssistUtils was using the default assistant app if Settings.Secure.ASSISTANT is
                // null, while only an empty string means user selected "None".
                if (setting != null) {
                    if (!setting.isEmpty()) {
                        ComponentName componentName = ComponentName.unflattenFromString(setting);
                        packageName = componentName != null ? componentName.getPackageName() : null;
                    } else {
                        packageName = null;
                    }
                } else if (mContext.getPackageManager().isDeviceUpgrading()) {
                    String defaultAssistant = mContext.getString(R.string.config_defaultAssistant);
                    packageName = !TextUtils.isEmpty(defaultAssistant) ? defaultAssistant : null;
                } else {
                    packageName = null;
                }
                return CollectionUtils.singletonOrEmpty(packageName);
            }
            case RoleManager.ROLE_BROWSER: {
                PackageManagerInternal packageManagerInternal = LocalServices.getService(
                        PackageManagerInternal.class);
                String packageName = packageManagerInternal.removeLegacyDefaultBrowserPackageName(
                        userId);
                return CollectionUtils.singletonOrEmpty(packageName);
            }
            case RoleManager.ROLE_DIALER: {
                String setting = Settings.Secure.getStringForUser(mContext.getContentResolver(),
                        Settings.Secure.DIALER_DEFAULT_APPLICATION, userId);
                String packageName;
                if (!TextUtils.isEmpty(setting)) {
                    packageName = setting;
                } else if (mContext.getPackageManager().isDeviceUpgrading()) {
                    // DefaultDialerManager was using the default dialer app if
                    // Settings.Secure.DIALER_DEFAULT_APPLICATION is invalid.
                    // TelecomManager.getSystemDialerPackage() won't work because it might not
                    // be ready.
                    packageName = mContext.getString(R.string.config_defaultDialer);
                } else {
                    packageName = null;
                }
                return CollectionUtils.singletonOrEmpty(packageName);
            }
            case RoleManager.ROLE_SMS: {
                String setting = Settings.Secure.getStringForUser(mContext.getContentResolver(),
                        Settings.Secure.SMS_DEFAULT_APPLICATION, userId);
                String packageName;
                if (!TextUtils.isEmpty(setting)) {
                    packageName = setting;
                } else if (mContext.getPackageManager().isDeviceUpgrading()) {
                    // SmsApplication was using the default SMS app if
                    // Settings.Secure.DIALER_DEFAULT_APPLICATION is invalid.
                    packageName = mContext.getString(R.string.config_defaultSms);
                } else {
                    packageName = null;
                }
                return CollectionUtils.singletonOrEmpty(packageName);
            }
            case RoleManager.ROLE_HOME: {
                PackageManager packageManager = mContext.getPackageManager();
                String packageName;
                if (packageManager.isDeviceUpgrading()) {
                    ResolveInfo resolveInfo = packageManager.resolveActivityAsUser(
                            new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME),
                            PackageManager.MATCH_DEFAULT_ONLY
                                    | PackageManager.MATCH_DIRECT_BOOT_AWARE
                                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
                    packageName = resolveInfo != null && resolveInfo.activityInfo != null
                            ? resolveInfo.activityInfo.packageName : null;
                    if (packageName != null && isSettingsApplication(packageName, userId)) {
                        packageName = null;
                    }
                } else {
                    packageName = null;
                }
                return CollectionUtils.singletonOrEmpty(packageName);
            }
            case RoleManager.ROLE_EMERGENCY: {
                String defaultEmergencyApp = Settings.Secure.getStringForUser(
                        mContext.getContentResolver(),
                        Settings.Secure.EMERGENCY_ASSISTANCE_APPLICATION, userId);
                return CollectionUtils.singletonOrEmpty(defaultEmergencyApp);
            }
            default: {
                Slog.e(LOG_TAG, "Don't know how to find legacy role holders for " + roleName);
                return Collections.emptyList();
            }
        }
    }

    private boolean isSettingsApplication(@NonNull String packageName, @UserIdInt int userId) {
        PackageManager packageManager = mContext.getPackageManager();
        ResolveInfo resolveInfo = packageManager.resolveActivityAsUser(new Intent(
                Settings.ACTION_SETTINGS), PackageManager.MATCH_DEFAULT_ONLY
                | PackageManager.MATCH_DIRECT_BOOT_AWARE
                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
        if (resolveInfo == null || resolveInfo.activityInfo == null) {
            return false;
        }
        return Objects.equals(packageName, resolveInfo.activityInfo.packageName);
    }
}
+291 −0
Original line number Diff line number Diff line
/*
 * 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 com.android.server.policy.role;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.role.RoleManager;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.os.Environment;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.Xml;

import com.android.internal.R;
import com.android.server.LocalServices;
import com.android.server.role.LegacyRoleStateProvider;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * Implementation to provide legacy role state.
 */
public class LegacyRoleStateProviderImpl implements LegacyRoleStateProvider {
    private static final String LOG_TAG = "LegacyRoleState";

    private static final String ROLES_FILE_NAME = "roles.xml";

    private static final String TAG_ROLES = "roles";
    private static final String TAG_ROLE = "role";
    private static final String TAG_HOLDER = "holder";
    private static final String ATTRIBUTE_NAME = "name";

    @NonNull
    private final Context mContext;

    public LegacyRoleStateProviderImpl(@NonNull Context context) {
        mContext = context;
    }

    @NonNull
    @Override
    public Map<String, Set<String>> getLegacyRoleState(@UserIdInt int userId) {
        Map<String, Set<String>> roles = readFile(userId);
        if (roles == null) {
            roles = readFromLegacySettings(userId);
        }
        return roles;
    }

    @Nullable
    private Map<String, Set<String>> readFile(@UserIdInt int userId) {
        File file = getFile(userId);
        try (FileInputStream in = new AtomicFile(file).openRead()) {
            XmlPullParser parser = Xml.newPullParser();
            parser.setInput(in, null);
            Map<String, Set<String>> roles = parseXml(parser);
            Slog.i(LOG_TAG, "Read legacy roles.xml successfully");
            return roles;
        } catch (FileNotFoundException e) {
            Slog.i(LOG_TAG, "Legacy roles.xml not found");
            return null;
        } catch (XmlPullParserException | IOException e) {
            Slog.wtf(LOG_TAG, "Failed to parse legacy roles.xml: " + file, e);
            return null;
        }
    }

    @NonNull
    private Map<String, Set<String>> parseXml(@NonNull XmlPullParser parser) throws IOException,
            XmlPullParserException {
        int type;
        int depth;
        int innerDepth = parser.getDepth() + 1;
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
            if (depth > innerDepth || type != XmlPullParser.START_TAG) {
                continue;
            }

            if (parser.getName().equals(TAG_ROLES)) {
                return parseRoles(parser);
            }
        }

        throw new IOException("Missing <" + TAG_ROLES + "> in roles.xml");
    }

    @NonNull
    private Map<String, Set<String>> parseRoles(@NonNull XmlPullParser parser) throws IOException,
            XmlPullParserException {
        Map<String, Set<String>> roles = new ArrayMap<>();

        int type;
        int depth;
        int innerDepth = parser.getDepth() + 1;
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
            if (depth > innerDepth || type != XmlPullParser.START_TAG) {
                continue;
            }

            if (parser.getName().equals(TAG_ROLE)) {
                String roleName = parser.getAttributeValue(null, ATTRIBUTE_NAME);
                Set<String> roleHolders = parseRoleHoldersLocked(parser);
                roles.put(roleName, roleHolders);
            }
        }

        return roles;
    }

    @NonNull
    private Set<String> parseRoleHoldersLocked(@NonNull XmlPullParser parser)
            throws IOException, XmlPullParserException {
        Set<String> roleHolders = new ArraySet<>();

        int type;
        int depth;
        int innerDepth = parser.getDepth() + 1;
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
            if (depth > innerDepth || type != XmlPullParser.START_TAG) {
                continue;
            }

            if (parser.getName().equals(TAG_HOLDER)) {
                String roleHolder = parser.getAttributeValue(null, ATTRIBUTE_NAME);
                roleHolders.add(roleHolder);
            }
        }

        return roleHolders;
    }

    @NonNull
    private static File getFile(@UserIdInt int userId) {
        return new File(Environment.getUserSystemDirectory(userId), ROLES_FILE_NAME);
    }

    @NonNull
    private Map<String, Set<String>> readFromLegacySettings(@UserIdInt int userId) {
        Map<String, Set<String>> roles = new ArrayMap<>();

        // Assistant
        ContentResolver contentResolver = mContext.getContentResolver();
        String assistantSetting = Settings.Secure.getStringForUser(contentResolver,
                Settings.Secure.ASSISTANT, userId);
        PackageManager packageManager = mContext.getPackageManager();
        String assistantPackageName;
        // AssistUtils was using the default assistant app if Settings.Secure.ASSISTANT is
        // null, while only an empty string means user selected "None".
        if (assistantSetting != null) {
            if (!assistantSetting.isEmpty()) {
                ComponentName componentName = ComponentName.unflattenFromString(assistantSetting);
                assistantPackageName = componentName != null ? componentName.getPackageName()
                        : null;
            } else {
                assistantPackageName = null;
            }
        } else if (packageManager.isDeviceUpgrading()) {
            String defaultAssistant = mContext.getString(R.string.config_defaultAssistant);
            assistantPackageName = !TextUtils.isEmpty(defaultAssistant) ? defaultAssistant : null;
        } else {
            assistantPackageName = null;
        }
        if (assistantPackageName != null) {
            roles.put(RoleManager.ROLE_ASSISTANT, Collections.singleton(assistantPackageName));
        }

        // Browser
        PackageManagerInternal packageManagerInternal = LocalServices.getService(
                PackageManagerInternal.class);
        String browserPackageName = packageManagerInternal.removeLegacyDefaultBrowserPackageName(
                userId);
        if (browserPackageName != null) {
            roles.put(RoleManager.ROLE_BROWSER, Collections.singleton(browserPackageName));
        }

        // Dialer
        String dialerSetting = Settings.Secure.getStringForUser(contentResolver,
                Settings.Secure.DIALER_DEFAULT_APPLICATION, userId);
        String dialerPackageName;
        if (!TextUtils.isEmpty(dialerSetting)) {
            dialerPackageName = dialerSetting;
        } else if (packageManager.isDeviceUpgrading()) {
            // DefaultDialerManager was using the default dialer app if
            // Settings.Secure.DIALER_DEFAULT_APPLICATION is invalid.
            // TelecomManager.getSystemDialerPackage() won't work because it might not
            // be ready.
            dialerPackageName = mContext.getString(R.string.config_defaultDialer);
        } else {
            dialerPackageName = null;
        }
        if (dialerPackageName != null) {
            roles.put(RoleManager.ROLE_DIALER, Collections.singleton(dialerPackageName));
        }

        // SMS
        String smsSetting = Settings.Secure.getStringForUser(contentResolver,
                Settings.Secure.SMS_DEFAULT_APPLICATION, userId);
        String smsPackageName;
        if (!TextUtils.isEmpty(smsSetting)) {
            smsPackageName = smsSetting;
        } else if (mContext.getPackageManager().isDeviceUpgrading()) {
            // SmsApplication was using the default SMS app if
            // Settings.Secure.DIALER_DEFAULT_APPLICATION is invalid.
            smsPackageName = mContext.getString(R.string.config_defaultSms);
        } else {
            smsPackageName = null;
        }
        if (smsPackageName != null) {
            roles.put(RoleManager.ROLE_SMS, Collections.singleton(smsPackageName));
        }

        // Home
        String homePackageName;
        if (packageManager.isDeviceUpgrading()) {
            ResolveInfo resolveInfo = packageManager.resolveActivityAsUser(
                    new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME),
                    PackageManager.MATCH_DEFAULT_ONLY
                            | PackageManager.MATCH_DIRECT_BOOT_AWARE
                            | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
            homePackageName = resolveInfo != null && resolveInfo.activityInfo != null
                    ? resolveInfo.activityInfo.packageName : null;
            if (homePackageName != null && isSettingsApplication(homePackageName, userId)) {
                homePackageName = null;
            }
        } else {
            homePackageName = null;
        }
        if (homePackageName != null) {
            roles.put(RoleManager.ROLE_HOME, Collections.singleton(homePackageName));
        }

        // Emergency
        String emergencyPackageName = Settings.Secure.getStringForUser(contentResolver,
                Settings.Secure.EMERGENCY_ASSISTANCE_APPLICATION, userId);
        if (emergencyPackageName != null) {
            roles.put(RoleManager.ROLE_EMERGENCY, Collections.singleton(emergencyPackageName));
        }

        return roles;
    }

    private boolean isSettingsApplication(@NonNull String packageName, @UserIdInt int userId) {
        PackageManager packageManager = mContext.getPackageManager();
        ResolveInfo resolveInfo = packageManager.resolveActivityAsUser(new Intent(
                Settings.ACTION_SETTINGS), PackageManager.MATCH_DEFAULT_ONLY
                | PackageManager.MATCH_DIRECT_BOOT_AWARE
                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
        if (resolveInfo == null || resolveInfo.activityInfo == null) {
            return false;
        }
        return Objects.equals(packageName, resolveInfo.activityInfo.packageName);
    }
}
+13 −10
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 * Copyright (C) 2021 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.
@@ -19,22 +19,25 @@ package com.android.server.role;
import android.annotation.NonNull;
import android.annotation.UserIdInt;

import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * A provider for migrating legacy "role"s to their actual role implementation.
 * Provider for legacy role state.
 * <p>
 * The role state may come from two sources, either the different pre-role default app settings, or
 * the pre-modularization roles.xml file stored in platform.
 *
 * @hide
 */
//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
public interface LegacyRoleHolderProvider {
public interface LegacyRoleStateProvider {
    /**
     * Get the list of holders of a legacy "role" before its actual role is introduced.
     * <p>
     * This method will only be called for the first time a role is made available in the platform.
     * Get the legacy role state stored in the platform.
     *
     * @param roleName the name of the role
     * @param userId the user ID
     * @return a list of holders for the given role
     * @return a mapping of role name to its set of holders
     */
    @NonNull
    List<String> getLegacyRoleHolders(@NonNull String roleName, @UserIdInt int userId);
    Map<String, Set<String>> getLegacyRoleState(@UserIdInt int userId);
}
+4 −31
Original line number Diff line number Diff line
@@ -103,7 +103,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
    private final Object mLock = new Object();

    @NonNull
    private final LegacyRoleHolderProvider mLegacyRoleHolderProvider;
    private final LegacyRoleStateProvider mLegacyRoleStateProvider;

    /**
     * Maps user id to its state.
@@ -139,10 +139,10 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
            new SparseArray<>();

    public RoleManagerService(@NonNull Context context,
            @NonNull LegacyRoleHolderProvider legacyRoleHolderProvider) {
            @NonNull LegacyRoleStateProvider legacyRoleStateProvider) {
        super(context);

        mLegacyRoleHolderProvider = legacyRoleHolderProvider;
        mLegacyRoleStateProvider = legacyRoleStateProvider;

        RoleControllerManager.initializeRemoteServiceComponentName(context);

@@ -241,16 +241,6 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
            return AndroidFuture.completedFuture(null);
        }

        //TODO gradually add more role migrations statements here for remaining roles
        // Make sure to implement LegacyRoleResolutionPolicy#getRoleHolders
        // for a given role before adding a migration statement for it here
        maybeMigrateRole(RoleManager.ROLE_ASSISTANT, userId);
        maybeMigrateRole(RoleManager.ROLE_BROWSER, userId);
        maybeMigrateRole(RoleManager.ROLE_DIALER, userId);
        maybeMigrateRole(RoleManager.ROLE_SMS, userId);
        maybeMigrateRole(RoleManager.ROLE_EMERGENCY, userId);
        maybeMigrateRole(RoleManager.ROLE_HOME, userId);

        // Some package state has changed, so grant default roles again.
        Slog.i(LOG_TAG, "Granting default roles...");
        AndroidFuture<Void> future = new AndroidFuture<>();
@@ -266,23 +256,6 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
        return future;
    }

    private void maybeMigrateRole(String role, @UserIdInt int userId) {
        // Any role for which we have a record are already migrated
        RoleUserState userState = getOrCreateUserState(userId);
        if (!userState.isRoleAvailable(role)) {
            List<String> roleHolders = mLegacyRoleHolderProvider.getLegacyRoleHolders(role, userId);
            if (roleHolders.isEmpty()) {
                return;
            }
            Slog.i(LOG_TAG, "Migrating " + role + ", legacy holders: " + roleHolders);
            userState.addRoleName(role);
            int size = roleHolders.size();
            for (int i = 0; i < size; i++) {
                userState.addRoleHolder(role, roleHolders.get(i));
            }
        }
    }

    @Nullable
    private String computePackageStateHash(@UserIdInt int userId) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
@@ -327,7 +300,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
        synchronized (mLock) {
            RoleUserState userState = mUserStates.get(userId);
            if (userState == null) {
                userState = new RoleUserState(userId, this);
                userState = new RoleUserState(userId, mLegacyRoleStateProvider, this);
                mUserStates.put(userId, userState);
            }
            return userState;
+19 −112

File changed.

Preview size limit exceeded, changes collapsed.

Loading