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

Commit 1df15af9 authored by Nate Myren's avatar Nate Myren Committed by Philip P. Moltmann
Browse files

Resurrect the ReviewOngoingPermissionsFragment

Resurrect the fragment, its data model, and allow the
PermissionController to handle its intent

Test: Manual
Bug: 159998363
Change-Id: I9a95528e333982e4f80a8c8805bedb58d6f77bf6
parent 9d84bee3
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -122,6 +122,7 @@
                <action android:name="android.intent.action.MANAGE_APP_PERMISSION" />
                <action android:name="android.intent.action.MANAGE_PERMISSION_APPS" />
                <action android:name="android.intent.action.MANAGE_PERMISSIONS" />
                <action android:name="android.intent.action.REVIEW_PERMISSION_USAGE" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
@@ -132,6 +133,7 @@
                  android:theme="@android:style/Theme.NoDisplay">
            <intent-filter android:priority="1">
                <action android:name="com.android.permissioncontroller.settingssearch.action.MANAGE_PERMISSION_APPS" />
                <action android:name="com.android.permissioncontroller.settingssearch.action.REVIEW_PERMISSION_USAGE" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
@@ -160,6 +162,17 @@
                  android:excludeFromRecents="true"
                  android:theme="@style/PermissionDialog.FilterTouches" />

        <activity android:name="com.android.permissioncontroller.permission.debug.ReviewOngoingUsageActivity"
                  android:excludeFromRecents="true"
                  android:theme="@style/PermissionDialog"
                  android:launchMode="singleInstance"
                  android:permission="android.permission.GRANT_RUNTIME_PERMISSIONS" >
            <intent-filter android:priority="1">
                <action android:name="android.intent.action.REVIEW_ONGOING_PERMISSION_USAGE" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>

        <activity android:name="com.android.permissioncontroller.permission.ui.ReviewAccessibilityServicesActivity"
                  android:excludeFromRecents="true"
                  android:theme="@style/PermissionDialog.FilterTouches"
+40 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
     Copyright (C) 2019 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.
-->

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
            android:id="@+id/container"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            style="@style/PermissionUsageDialogContainerScrollView">

    <LinearLayout
        android:id="@+id/dialog_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        style="@style/PermissionUsageDialogContainerLayout">

        <TextView
            android:id="@+id/title"
            style="@style/PermissionUsageDialogTitle"/>

        <LinearLayout
            android:id="@+id/items_container"
            style="@style/PermissionUsageDialogItemsContainer"/>

    </LinearLayout>

</ScrollView>
+38 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
  ~ 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.
  -->

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              style="@style/PermissionUsageDialogItemContainer">

    <FrameLayout
        style="@style/PermissionUsageDialogItemIconFrame">

        <ImageView
            android:id="@+id/app_icon"
            style="@style/PermissionUsageDialogItemIcon"/>

    </FrameLayout>

    <TextView
        android:id="@+id/app_name"
        style="@style/PermissionUsageDialogItemAppName"/>

    <LinearLayout
        android:id="@+id/icons"
        style="@style/PermissionUsageDialogItemIconsContainer"/>

</LinearLayout>
 No newline at end of file
+15 −0
Original line number Diff line number Diff line
@@ -740,6 +740,21 @@
    <!-- Label for the button to set an application as the default application [CHAR LIMIT=20] -->
    <string name="request_role_set_as_default">Set as default</string>

    <!-- Action for accepting the Ongoing usage dialog [CHAR LIMIT=10]-->
    <string name="ongoing_usage_dialog_ok">Got it</string>

    <!-- Action on Ongoing usage dialog to open the Privacy settings. [CHAR LIMIT=23]-->
    <string name="ongoing_usage_dialog_open_settings">Privacy settings</string>

    <!-- Title for Ongoing usage dialog title when multiple apps are using app ops [CHAR LIMIT=NONE] -->
    <string name="ongoing_usage_dialog_title">Apps using your <xliff:g id="types_list" example="camera( and location)">%s</xliff:g></string>

    <!-- Separator for permissions. Include spaces before and after if needed [CHAR LIMIT=10] -->
    <string name="ongoing_usage_dialog_separator">,\u0020</string>

    <!-- Separator for permissions, before last type. Include spaces before and after if needed [CHAR LIMIT=10] -->
    <string name="ongoing_usage_dialog_last_separator">\u0020and\u0020</string>

    <!-- Keyword in the Settings app's search functionality that can be used to find links to the default app management screens [CHAR LIMIT=none] -->
    <string name="default_app_search_keyword">default apps</string>

+259 −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.permissioncontroller.permission.debug;

import android.app.AppOpsManager;
import android.app.AppOpsManager.HistoricalOp;
import android.app.AppOpsManager.HistoricalPackageOps;
import android.app.AppOpsManager.OpEntry;
import android.app.AppOpsManager.PackageOps;

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

import com.android.permissioncontroller.permission.model.AppPermissionGroup;
import com.android.permissioncontroller.permission.model.Permission;
import com.android.permissioncontroller.permission.model.legacy.PermissionApps.PermissionApp;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;

/**
 * Stats for permission usage of an app. This data is for a given time period,
 * i.e. does not contain the full history.
 */
public final class AppPermissionUsage {
    private final @NonNull List<GroupUsage> mGroupUsages = new ArrayList<>();
    private final @NonNull PermissionApp mPermissionApp;

    private AppPermissionUsage(@NonNull PermissionApp permissionApp,
            @NonNull List<AppPermissionGroup> groups, @Nullable PackageOps lastUsage,
            @Nullable HistoricalPackageOps historicalUsage) {
        mPermissionApp = permissionApp;
        final int groupCount = groups.size();
        for (int i = 0; i < groupCount; i++) {
            final AppPermissionGroup group = groups.get(i);
            mGroupUsages.add(new GroupUsage(group, lastUsage, historicalUsage));
        }
    }

    public @NonNull PermissionApp getApp() {
        return mPermissionApp;
    }

    public @NonNull String getPackageName() {
        return mPermissionApp.getPackageName();
    }

    public int getUid() {
        return mPermissionApp.getUid();
    }

    public long getLastAccessTime() {
        long lastAccessTime = 0;
        final int permissionCount = mGroupUsages.size();
        for (int i = 0; i < permissionCount; i++) {
            final GroupUsage groupUsage = mGroupUsages.get(i);
            lastAccessTime = Math.max(lastAccessTime, groupUsage.getLastAccessTime());
        }
        return lastAccessTime;
    }

    public long getAccessCount() {
        long accessCount = 0;
        final int permissionCount = mGroupUsages.size();
        for (int i = 0; i < permissionCount; i++) {
            final GroupUsage permission = mGroupUsages.get(i);
            accessCount += permission.getAccessCount();
        }
        return accessCount;
    }

    public @NonNull List<GroupUsage> getGroupUsages() {
        return mGroupUsages;
    }

    /**
     * Stats for permission usage of a permission group. This data is for a
     * given time period, i.e. does not contain the full history.
     */
    public static class GroupUsage {
        private final @NonNull AppPermissionGroup mGroup;
        private final @Nullable PackageOps mLastUsage;
        private final @Nullable HistoricalPackageOps mHistoricalUsage;

        GroupUsage(@NonNull AppPermissionGroup group, @Nullable PackageOps lastUsage,
                @Nullable HistoricalPackageOps historicalUsage) {
            mGroup = group;
            mLastUsage = lastUsage;
            mHistoricalUsage = historicalUsage;
        }

        public long getLastAccessTime() {
            if (mLastUsage == null) {
                return 0;
            }
            return lastAccessAggregate(
                    (op) -> op.getLastAccessTime(AppOpsManager.OP_FLAGS_ALL_TRUSTED));
        }

        public long getLastAccessForegroundTime() {
            if (mLastUsage == null) {
                return 0;
            }
            return lastAccessAggregate(
                    (op) -> op.getLastAccessForegroundTime(AppOpsManager.OP_FLAGS_ALL_TRUSTED));
        }

        public long getLastAccessBackgroundTime() {
            if (mLastUsage == null) {
                return 0;
            }
            return lastAccessAggregate(
                    (op) -> op.getLastAccessBackgroundTime(AppOpsManager.OP_FLAGS_ALL_TRUSTED));
        }

        public long getForegroundAccessCount() {
            if (mHistoricalUsage == null) {
                return 0;
            }
            return extractAggregate((HistoricalOp op)
                    -> op.getForegroundAccessCount(AppOpsManager.OP_FLAGS_ALL_TRUSTED));
        }

        public long getBackgroundAccessCount() {
            if (mHistoricalUsage == null) {
                return 0;
            }
            return extractAggregate((HistoricalOp op)
                    -> op.getBackgroundAccessCount(AppOpsManager.OP_FLAGS_ALL_TRUSTED));
        }

        public long getAccessCount() {
            if (mHistoricalUsage == null) {
                return 0;
            }
            return extractAggregate((HistoricalOp op) ->
                    op.getForegroundAccessCount(AppOpsManager.OP_FLAGS_ALL_TRUSTED)
                            + op.getBackgroundAccessCount(AppOpsManager.OP_FLAGS_ALL_TRUSTED)
            );
        }

        public long getAccessDuration() {
            if (mHistoricalUsage == null) {
                return 0;
            }
            return extractAggregate((HistoricalOp op) ->
                    op.getForegroundAccessDuration(AppOpsManager.OP_FLAGS_ALL_TRUSTED)
                            + op.getBackgroundAccessDuration(AppOpsManager.OP_FLAGS_ALL_TRUSTED)
            );
        }

        public boolean isRunning() {
            if (mLastUsage == null) {
                return false;
            }
            final ArrayList<Permission> permissions = mGroup.getPermissions();
            final int permissionCount = permissions.size();
            for (int i = 0; i < permissionCount; i++) {
                final Permission permission = permissions.get(i);
                final String opName = permission.getAppOp();
                final List<OpEntry> ops = mLastUsage.getOps();
                final int opCount = ops.size();
                for (int j = 0; j < opCount; j++) {
                    final OpEntry op = ops.get(j);
                    if (op.getOpStr().equals(opName) && op.isRunning()) {
                        return true;
                    }
                }
            }
            return false;
        }

        private long extractAggregate(@NonNull Function<HistoricalOp, Long> extractor) {
            long aggregate = 0;
            final ArrayList<Permission> permissions = mGroup.getPermissions();
            final int permissionCount = permissions.size();
            for (int i = 0; i < permissionCount; i++) {
                final Permission permission = permissions.get(i);
                final String opName = permission.getAppOp();
                final HistoricalOp historicalOp = mHistoricalUsage.getOp(opName);
                if (historicalOp != null) {
                    aggregate += extractor.apply(historicalOp);
                }
            }
            return aggregate;
        }

        private long lastAccessAggregate(@NonNull Function<OpEntry, Long> extractor) {
            long aggregate = 0;
            final ArrayList<Permission> permissions = mGroup.getPermissions();
            final int permissionCount = permissions.size();
            for (int permissionNum = 0; permissionNum < permissionCount; permissionNum++) {
                final Permission permission = permissions.get(permissionNum);
                final String opName = permission.getAppOp();
                final List<OpEntry> ops = mLastUsage.getOps();
                final int opCount = ops.size();
                for (int opNum = 0; opNum < opCount; opNum++) {
                    final OpEntry op = ops.get(opNum);
                    if (op.getOpStr().equals(opName)) {
                        aggregate = Math.max(aggregate, extractor.apply(op));
                    }
                }
            }
            return aggregate;
        }

        public @NonNull AppPermissionGroup getGroup() {
            return mGroup;
        }
    }

    public static class Builder {
        private final @NonNull List<AppPermissionGroup> mGroups = new ArrayList<>();
        private final @NonNull PermissionApp mPermissionApp;
        private @Nullable PackageOps mLastUsage;
        private @Nullable HistoricalPackageOps mHistoricalUsage;

        public Builder(@NonNull PermissionApp permissionApp) {
            mPermissionApp = permissionApp;
        }

        public @NonNull Builder addGroup(@NonNull AppPermissionGroup group) {
            mGroups.add(group);
            return this;
        }

        public @NonNull Builder setLastUsage(@Nullable PackageOps lastUsage) {
            mLastUsage = lastUsage;
            return this;
        }

        public @NonNull Builder setHistoricalUsage(@Nullable HistoricalPackageOps historicalUsage) {
            mHistoricalUsage = historicalUsage;
            return this;
        }

        public @NonNull AppPermissionUsage build() {
            if (mGroups.isEmpty()) {
                throw new IllegalStateException("mGroups cannot be empty.");
            }
            return new AppPermissionUsage(mPermissionApp, mGroups, mLastUsage, mHistoricalUsage);
        }
    }
}
 No newline at end of file
Loading