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

Commit fa477408 authored by Hai Zhang's avatar Hai Zhang
Browse files

Add role to PermissionController.

A role is a unique name within the system associated with certain
privileges. There can be multiple applications qualifying for a role,
but only a subset of them can become role holders. To qualify for a
role, an application must meet certain requirements, including
defining certain components in its manifest. Then the application will
need user consent to become the role holder.

Upon becoming a role holder, the application may be granted certain
privileges that are role specific. When an application loses its role,
these privileges will also be revoked.

Bug: 110557011
Test: build
Change-Id: I15fbae29e4a98f649a9c5cae5a4c1e51f7d1b3c2
parent b136004a
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@
    <uses-permission android:name="android.permission.MANAGE_APP_OPS_RESTRICTIONS" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <uses-permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS" />
    <uses-permission android:name="android.permission.MANAGE_ROLE_HOLDERS" />

    <uses-permission android:name="android.permission.ACCESS_INSTANT_APPS" />

@@ -76,6 +77,13 @@
                <action android:name="android.permissionpresenterservice.RuntimePermissionPresenterService"/>
            </intent-filter>
        </service>

        <service android:name="com.android.packageinstaller.role.service.RoleControllerServiceImpl"
                 android:permission="android.permission.BIND_ROLE_CONTROLLER_SERVICE">
            <intent-filter android:priority="1">
                <action android:name="android.rolecontrollerservice.RoleControllerService"/>
            </intent-filter>
        </service>
    </application>

</manifest>

res/xml/roles.xml

0 → 100644
+238 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>

<!-- 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.
-->

<roles>

    <permission-set name="phone">
        <permission name="android.permission.READ_PHONE_STATE" />
        <permission name="android.permission.CALL_PHONE" />
        <permission name="android.permission.READ_CALL_LOG" />
        <permission name="android.permission.WRITE_CALL_LOG" />
        <permission name="com.android.voicemail.permission.ADD_VOICEMAIL" />
        <permission name="android.permission.USE_SIP" />
        <permission name="android.permission.PROCESS_OUTGOING_CALLS" />
    </permission-set>

    <permission-set name="contacts">
        <permission name="android.permission.READ_CONTACTS" />
        <permission name="android.permission.WRITE_CONTACTS" />
        <permission name="android.permission.GET_ACCOUNTS" />
    </permission-set>

    <permission-set name="location">
        <permission name="android.permission.ACCESS_FINE_LOCATION" />
        <permission name="android.permission.ACCESS_COARSE_LOCATION" />
    </permission-set>

    <permission-set name="coarse_location">
        <permission name="android.permission.ACCESS_COARSE_LOCATION" />
    </permission-set>

    <permission-set name="calendar">
        <permission name="android.permission.READ_CALENDAR" />
        <permission name="android.permission.WRITE_CALENDAR" />
    </permission-set>

    <permission-set name="sms">
        <permission name="android.permission.SEND_SMS" />
        <permission name="android.permission.RECEIVE_SMS" />
        <permission name="android.permission.READ_SMS" />
        <permission name="android.permission.RECEIVE_WAP_PUSH" />
        <permission name="android.permission.RECEIVE_MMS" />
        <permission name="android.permission.READ_CELL_BROADCASTS" />
    </permission-set>

    <permission-set name="microphone">
        <permission name="android.permission.RECORD_AUDIO" />
    </permission-set>

    <permission-set name="camera">
        <permission name="android.permission.CAMERA" />
    </permission-set>

    <permission-set name="sensors">
        <permission name="android.permission.BODY_SENSORS" />
    </permission-set>

    <permission-set name="storage">
        <!-- STOPSHIP(b/112545973): remove once feature enabled by default -->
        <!--if (!SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) { -->
        <permission name="android.permission.READ_EXTERNAL_STORAGE" />
        <permission name="android.permission.WRITE_EXTERNAL_STORAGE" />
        <!-- } -->
    </permission-set>

    <permission-set name="media_aural">
        <!-- // STOPSHIP(b/112545973): remove once feature enabled by default -->
        <!-- if (SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) { -->
        <permission name="android.permission.READ_MEDIA_AUDIO" />
        <permission name="android.permission.WRITE_MEDIA_AUDIO" />
        <!-- } -->
    </permission-set>

    <permission-set name="media_visual">
        <!-- // STOPSHIP(b/112545973): remove once feature enabled by default -->
        <!-- if (SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) { -->
        <permission name="android.permission.READ_MEDIA_IMAGES" />
        <permission name="android.permission.WRITE_MEDIA_IMAGES" />
        <permission name="android.permission.READ_MEDIA_VIDEO" />
        <permission name="android.permission.WRITE_MEDIA_VIDEO" />
        <!-- } -->
    </permission-set>

    <!--- @see android.telecom.DefaultDialerManager -->
    <role name="android.app.role.DIALER">
        <required-components>
            <activity>
                <intent-filter>
                    <action name="android.intent.action.DIAL" />
                </intent-filter>
            </activity>
            <activity>
                <intent-filter>
                    <action name="android.intent.action.DIAL" />
                    <data scheme="tel" />
                </intent-filter>
            </activity>
        </required-components>
        <permissions>
            <permission-set name="phone" />
            <permission-set name="contacts" />
            <permission-set name="sms" />
            <permission-set name="microphone" />
            <permission-set name="camera" />
        </permissions>
        <preferred-activities>
            <preferred-activiy>
                <activity>
                    <intent-filter>
                        <action name="android.intent.action.DIAL" />
                        <data scheme="tel" />
                    </intent-filter>
                </activity>
                <!-- TODO: STOPSHIP: Set preferred? -->
                <intent-filter>
                    <action name="android.intent.action.DIAL" />
                    <data scheme="tel" />
                </intent-filter>
            </preferred-activiy>
        </preferred-activities>
    </role>

    <!--- @see com.android.internal.telephony.SmsApplication -->
    <role name="android.app.role.SMS">
        <required-components>
            <receiver permission="android.permission.BROADCAST_SMS">
                <intent-filter>
                    <action name="android.provider.Telephony.SMS_DELIVER" />
                </intent-filter>
            </receiver>
            <receiver permission="android.permission.BROADCAST_WAP_PUSH">
                <intent-filter>
                    <action name="android.provider.Telephony.WAP_PUSH_DELIVER" />
                    <data mimeType="application/vnd.wap.mms-message" />
                </intent-filter>
            </receiver>
            <service permission="android.permission.SEND_RESPOND_VIA_MESSAGE">
                <intent-filter>
                    <action name="android.intent.action.RESPOND_VIA_MESSAGE" />
                    <data scheme="smsto" />
                </intent-filter>
            </service>
            <activity>
                <intent-filter>
                    <action name="android.intent.action.SENDTO" />
                    <data scheme="smsto" />
                </intent-filter>
            </activity>
            <receiver>
                <intent-filter>
                    <action name="android.provider.action.DEFAULT_SMS_PACKAGE_CHANGED" />
                </intent-filter>
            </receiver>
            <receiver>
                <intent-filter>
                    <action name="android.provider.action.EXTERNAL_PROVIDER_CHANGE" />
                </intent-filter>
            </receiver>
            <receiver>
                <intent-filter>
                    <action name="android.provider.Telephony.SIM_FULL" />
                </intent-filter>
            </receiver>
        </required-components>
        <permissions>
            <permission-set name="phone" />
            <permission-set name="contacts" />
            <permission-set name="sms" />
            <permission-set name="storage" />
            <permission-set name="microphone" />
            <permission-set name="camera" />
        </permissions>
        <app-ops>
            <app-op name="android:read_sms" mode="allowed" />
            <app-op name="android:write_sms" mode="allowed" />
            <app-op name="android:receive_sms" mode="allowed" />
            <app-op name="android:receive_wap_push" mode="allowed" />
            <app-op name="android:send_sms" mode="allowed" />
            <app-op name="android:read_cell_broadcasts" mode="allowed" />
        </app-ops>
        <preferred-activities>
            <preferred-activiy>
                <activity>
                    <intent-filter>
                        <action name="android.intent.action.SENDTO" />
                        <data scheme="smsto" />
                    </intent-filter>
                </activity>
                <intent-filter>
                    <action name="android.intent.action.SENDTO" />
                    <data scheme="sms" />
                </intent-filter>
                <intent-filter>
                    <action name="android.intent.action.SENDTO" />
                    <data scheme="smsto" />
                </intent-filter>
                <intent-filter>
                    <action name="android.intent.action.SENDTO" />
                    <data scheme="mms" />
                </intent-filter>
                <intent-filter>
                    <action name="android.intent.action.SENDTO" />
                    <data scheme="mmsto" />
                </intent-filter>
            </preferred-activiy>
        </preferred-activities>
    </role>

    <!--- @see com.android.settings.applications.defaultapps.DefaultBrowserPreferenceController -->
    <role name="android.app.role.BROWSER">
        <required-components>
            <activity>
                <intent-filter>
                    <action name="android.intent.action.VIEW" />
                    <category name="android.intent.category.BROWSABLE" />
                    <data scheme="http" />
                </intent-filter>
            </activity>
        </required-components>
        <permissions>
            <permission-set name="location" />
        </permissions>
        <!-- TODO: STOPSHIP: Set preferred? -->
    </role>
</roles>
+104 −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.packageinstaller.role.model;

import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;

import androidx.annotation.NonNull;

import java.util.Objects;

/**
 * An app op to be granted or revoke by a {@link Role}.
 */
public class AppOp {

    /**
     * The name of this app op.
     */
    @NonNull
    private final String mName;

    /**
     * The mode of this app op when granted.
     */
    private final int mMode;

    public AppOp(String name, int mode) {
        mName = name;
        mMode = mode;
    }

    @NonNull
    public String getName() {
        return mName;
    }

    public int getMode() {
        return mMode;
    }

    /**
     * Grant this app op to an application by setting it to the mode specified in this object.
     *
     * @param applicationInfo the {@code ApplicationInfo} of the application
     * @param context the {@code Context} to retrieve the {@code AppOpManager}
     */
    public void grant(@NonNull ApplicationInfo applicationInfo, @NonNull Context context) {
        AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
        appOpsManager.setUidMode(mName, applicationInfo.uid, mMode);
    }

    /**
     * Revoke this app op from an application by resetting it back to its default state.
     *
     * @param applicationInfo the {@code ApplicationInfo} of the application
     * @param context the {@code Context} to retrieve the {@code AppOpManager}
     */
    public void revoke(@NonNull ApplicationInfo applicationInfo, @NonNull Context context) {
        AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
        appOpsManager.resetUidMode(mName, applicationInfo.uid, true);
    }

    @Override
    public String toString() {
        return "AppOp{"
                + "mName='" + mName + '\''
                + ", mMode=" + mMode
                + '}';
    }

    @Override
    public boolean equals(Object object) {
        if (this == object) {
            return true;
        }
        if (object == null || getClass() != object.getClass()) {
            return false;
        }
        AppOp appOp = (AppOp) object;
        return mMode == appOp.mMode
                && Objects.equals(mName, appOp.mName);
    }

    @Override
    public int hashCode() {
        return Objects.hash(mName, mMode);
    }
}
+167 −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.packageinstaller.role.model;

import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

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

/**
 * Specifies an {@code Intent} or an {@code IntentFilter} for matching application components.
 */
public class IntentFilterData {

    /**
     * The action of this {@code Intent} or {@code IntentFilter} specification. Exactly one action
     * is required so that we can create a single {@code Intent} with it.
     */
    @NonNull
    private final String mAction;

    /**
     * The categories of the {@code Intent} or {@code IntentFilter} specification. Should not
     * contain {@link Intent#CATEGORY_DEFAULT} as it will be automatically added when creating
     * {@code IntentFilter} instances (but not when creating {@code Intent} instances).
     */
    @NonNull
    private final List<String> mCategories;

    /**
     * Optional data scheme of the {@code Intent} or {@code IntentFilter} specification. At most one
     * data scheme is supported so that we can create a single {@code Intent} with it.
     */
    @Nullable
    private final String mDataScheme;

    /**
     * Optional data type of the {@code Intent} or {@code IntentFilter} specification. At most one
     * data type is supported so that we can create a single {@code Intent} with it.
     */
    @Nullable
    private final String mDataType;

    public IntentFilterData(@NonNull String action, @NonNull List<String> categories,
            @Nullable String dataScheme, @Nullable String dataType) {
        mAction = action;
        mCategories = categories;
        mDataScheme = dataScheme;
        mDataType = dataType;
    }

    @NonNull
    public String getAction() {
        return mAction;
    }

    @NonNull
    public List<String> getCategories() {
        return mCategories;
    }

    @Nullable
    public String getDataScheme() {
        return mDataScheme;
    }

    @Nullable
    public String getDataType() {
        return mDataType;
    }

    /**
     * Create an {@code Intent} with this specification.
     *
     * @return the {@code Intent} created
     */
    @NonNull
    public Intent createIntent() {
        Intent intent = new Intent(mAction);
        Uri data = mDataScheme != null ? Uri.fromParts(mDataScheme, "", null) : null;
        int categoriesSize = mCategories.size();
        for (int i = 0; i < categoriesSize; i++) {
            String category = mCategories.get(i);
            intent.addCategory(category);
        }
        intent.setDataAndType(data, mDataType);
        return intent;
    }

    /**
     * Create an {@code IntentFilter} with this specification. {@link Intent#CATEGORY_DEFAULT} will
     * be automatically added to the categories.
     *
     * @return the {@code IntentFilter} created
     */
    @NonNull
    public IntentFilter createIntentFilter() {
        IntentFilter intentFilter = new IntentFilter(mAction);
        intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
        int categoriesSize = mCategories.size();
        for (int i = 0; i < categoriesSize; i++) {
            String category = mCategories.get(i);
            intentFilter.addCategory(category);
        }
        if (mDataScheme != null) {
            intentFilter.addDataScheme(mDataScheme);
        }
        if (mDataType != null) {
            try {
                intentFilter.addDataType(mDataType);
            } catch (IntentFilter.MalformedMimeTypeException e) {
                // Should have been validated when parsing roles.
                throw new IllegalStateException(e);
            }
        }
        return intentFilter;
    }

    @Override
    public String toString() {
        return "IntentFilterData{"
                + "mAction='" + mAction + '\''
                + ", mCategories='" + mCategories + '\''
                + ", mDataScheme='" + mDataScheme + '\''
                + ", mDataType='" + mDataType + '\''
                + '}';
    }

    @Override
    public boolean equals(Object object) {
        if (this == object) {
            return true;
        }
        if (object == null || getClass() != object.getClass()) {
            return false;
        }
        IntentFilterData that = (IntentFilterData) object;
        return Objects.equals(mAction, that.mAction)
                && Objects.equals(mCategories, that.mCategories)
                && Objects.equals(mDataScheme, that.mDataScheme)
                && Objects.equals(mDataType, that.mDataType);
    }

    @Override
    public int hashCode() {
        return Objects.hash(mAction, mCategories, mDataScheme, mDataType);
    }
}
+81 −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.packageinstaller.role.model;

import androidx.annotation.NonNull;

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

/**
 * A set of permissions with a name to be granted by a {@link Role}.
 */
public class PermissionSet {

    /**
     * The name of this permission set. Must be unique.
     */
    @NonNull
    private final String mName;

    /**
     * The permissions of this permission set.
     */
    @NonNull
    private final List<String> mPermissions;

    public PermissionSet(@NonNull String name, @NonNull List<String> permissions) {
        mName = name;
        mPermissions = permissions;
    }

    @NonNull
    public String getName() {
        return mName;
    }

    @NonNull
    public List<String> getPermissions() {
        return mPermissions;
    }

    @Override
    public String toString() {
        return "PermissionSet{"
                + "mName='" + mName + '\''
                + ", mPermissions=" + mPermissions
                + '}';
    }

    @Override
    public boolean equals(Object object) {
        if (this == object) {
            return true;
        }
        if (object == null || getClass() != object.getClass()) {
            return false;
        }
        PermissionSet that = (PermissionSet) object;
        return Objects.equals(mName, that.mName)
                && Objects.equals(mPermissions, that.mPermissions);
    }

    @Override
    public int hashCode() {
        return Objects.hash(mName, mPermissions);
    }
}
Loading