Merge "Import translations. DO NOT MERGE ANYWHERE" into sc-dev
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/HibernationSettingStateLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/HibernationSettingStateLiveData.kt
index bb49e15..1868687 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/HibernationSettingStateLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/HibernationSettingStateLiveData.kt
@@ -27,7 +27,6 @@
 import com.android.permissioncontroller.permission.model.livedatatypes.HibernationSettingState
 import com.android.permissioncontroller.hibernation.ExemptServicesLiveData
 import com.android.permissioncontroller.hibernation.HibernationEnabledLiveData
-import com.android.permissioncontroller.hibernation.isHibernationEnabled
 import com.android.permissioncontroller.hibernation.isHibernationJobEnabled
 import com.android.permissioncontroller.hibernation.isPackageHibernationExemptByUser
 import com.android.permissioncontroller.hibernation.isPackageHibernationExemptBySystem
@@ -102,8 +101,7 @@
             }
         }
 
-        postValue(HibernationSettingState(isHibernationJobEnabled(), canHibernate, revocableGroups,
-            isHibernationEnabled() || revocableGroups.isNotEmpty()))
+        postValue(HibernationSettingState(isHibernationJobEnabled(), canHibernate, revocableGroups))
     }
 
     override fun onOpChanged(op: String?, packageName: String?) {
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/AppPermissionUsage.java b/PermissionController/src/com/android/permissioncontroller/permission/model/AppPermissionUsage.java
index eec91f0..e9e5a99 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/model/AppPermissionUsage.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/model/AppPermissionUsage.java
@@ -20,23 +20,31 @@
 
 import android.Manifest;
 import android.app.AppOpsManager;
+import android.app.AppOpsManager.AttributedHistoricalOps;
+import android.app.AppOpsManager.AttributedOpEntry;
 import android.app.AppOpsManager.HistoricalOp;
 import android.app.AppOpsManager.HistoricalPackageOps;
 import android.app.AppOpsManager.OpEntry;
 import android.app.AppOpsManager.OpEventProxyInfo;
 import android.app.AppOpsManager.PackageOps;
+import android.content.pm.Attribution;
 import android.media.AudioRecordingConfiguration;
+import android.os.Build;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
 
 import com.android.permissioncontroller.permission.model.legacy.PermissionApps.PermissionApp;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.function.Function;
+import java.util.stream.Collectors;
 
 import kotlin.Triple;
 
@@ -129,7 +137,7 @@
      * 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 {
+    public static class GroupUsage implements TimelineUsage {
         private final @NonNull AppPermissionGroup mGroup;
         private final @Nullable PackageOps mLastUsage;
         private final @Nullable HistoricalPackageOps mHistoricalUsage;
@@ -219,9 +227,7 @@
         }
 
 
-        /**
-         * returns whether the usage has discrete data
-         */
+        @Override
         public boolean hasDiscreteData() {
             if (mHistoricalUsage == null) {
                 return false;
@@ -234,14 +240,10 @@
                     return true;
                 }
             }
-
             return false;
         }
 
-        /**
-         * get all discrete access time in millis
-         * Returns a list of triples of (access time, access duration, proxy)
-         */
+        @Override
         public List<Triple<Long, Long, OpEventProxyInfo>> getAllDiscreteAccessTime() {
             List<Triple<Long, Long, OpEventProxyInfo>> allDiscreteAccessTime = new ArrayList<>();
             if (!hasDiscreteData()) {
@@ -340,13 +342,17 @@
             return allOps;
         }
 
+        @Override
         public @NonNull AppPermissionGroup getGroup() {
             return mGroup;
         }
 
-        /**
-         * Returns attribution tags for the historical usage.
-         */
+        @Override
+        public int getLabel() {
+            return -1;
+        }
+
+        @Override
         public @Nullable ArrayList<String> getAttributionTags() {
             if (mHistoricalUsage == null || mHistoricalUsage.getAttributedOpsCount() == 0) {
                 return null;
@@ -358,6 +364,143 @@
             }
             return attributionTags;
         }
+
+        /** Creates a lookup from the attribution tag to its label. **/
+        @RequiresApi(Build.VERSION_CODES.S)
+        private static Map<String, Integer> getAttributionTagToLabelMap(
+                Attribution[] attributions) {
+            Map<String, Integer> attributionTagToLabelMap = new HashMap<>();
+            for (Attribution attribution : attributions) {
+                attributionTagToLabelMap.put(attribution.getTag(), attribution.getLabel());
+            }
+            return attributionTagToLabelMap;
+        }
+
+        /** Partitions the usages based on the attribution tag label. */
+        @RequiresApi(Build.VERSION_CODES.S)
+        public List<AttributionLabelledGroupUsage> getAttributionLabelledGroupUsages() {
+            Map<String, Integer> attributionTagToLabelMap =
+                    getAttributionTagToLabelMap(getGroup().getApp().attributions);
+
+            Set<String> allOps = getAllOps(mGroup);
+
+            // we need to collect discreteAccessTime for each label
+            Map<Integer, AttributionLabelledGroupUsage.Builder> labelDiscreteAccessMap =
+                    new HashMap<>();
+
+            for (int i = 0; i < mHistoricalUsage.getAttributedOpsCount(); i++) {
+                AttributedHistoricalOps attributedOp = mHistoricalUsage.getAttributedOpsAt(i);
+                String attributionTag = attributedOp.getTag();
+
+                for (String opName : allOps) {
+                    final HistoricalOp historicalOp = attributedOp.getOp(opName);
+                    if (historicalOp == null) {
+                        continue;
+                    }
+
+                    int discreteAccessCount = historicalOp.getDiscreteAccessCount();
+                    for (int j = 0; j < discreteAccessCount; j++) {
+                        AttributedOpEntry opEntry = historicalOp.getDiscreteAccessAt(j);
+                        Integer label = attributionTagToLabelMap.get(attributedOp.getTag());
+                        if (!labelDiscreteAccessMap.containsKey(label)) {
+                            labelDiscreteAccessMap.put(label,
+                                    new AttributionLabelledGroupUsage.Builder(label, getGroup()));
+                        }
+                        labelDiscreteAccessMap.get(label).addAttributionTag(attributionTag);
+                        labelDiscreteAccessMap.get(label).addDiscreteAccessTime(new Triple<>(
+                                opEntry.getLastAccessTime(PRIVACY_HUB_FLAGS),
+                                opEntry.getLastDuration(PRIVACY_HUB_FLAGS),
+                                opEntry.getLastProxyInfo(PRIVACY_HUB_FLAGS)));
+                    }
+                }
+            }
+
+            return labelDiscreteAccessMap.entrySet().stream()
+                    .map(e -> e.getValue().build())
+                    .collect(Collectors.toList());
+        }
+
+        /**
+         * Represents the slice of {@link GroupUsage} with a label.
+         *
+         * <p> -1 as label means that there was no entry for the attribution tag in the
+         * manifest.</p>
+         */
+        public static class AttributionLabelledGroupUsage implements TimelineUsage {
+            private final int mLabel;
+            private final AppPermissionGroup mAppPermissionGroup;
+            private final List<String> mAttributionTags;
+            private final List<Triple<Long, Long, OpEventProxyInfo>> mDiscreteAccessTime;
+
+            AttributionLabelledGroupUsage(int label,
+                    AppPermissionGroup appPermissionGroup,
+                    List<String> attributionTags,
+                    List<Triple<Long, Long, OpEventProxyInfo>> discreteAccessTime) {
+                mLabel = label;
+                mAppPermissionGroup = appPermissionGroup;
+                mAttributionTags = attributionTags;
+                mDiscreteAccessTime = discreteAccessTime;
+            }
+
+            @Override
+            public int getLabel() {
+                return mLabel;
+            }
+
+            @Override
+            public boolean hasDiscreteData() {
+                return mDiscreteAccessTime.size() > 0;
+            }
+
+            @Override
+            public List<Triple<Long, Long, OpEventProxyInfo>> getAllDiscreteAccessTime() {
+                return mDiscreteAccessTime;
+            }
+
+            @Override
+            public List<String> getAttributionTags() {
+                return mAttributionTags;
+            }
+
+            @Override
+            public AppPermissionGroup getGroup() {
+                return mAppPermissionGroup;
+            }
+
+            static class Builder {
+                private final int mLabel;
+                private final AppPermissionGroup mAppPermissionGroup;
+                private Set<String> mAttributionTags;
+                private List<Triple<Long, Long, OpEventProxyInfo>>  mDiscreteAccessTime;
+
+                Builder(int label, AppPermissionGroup appPermissionGroup) {
+                    mLabel = label;
+                    mAppPermissionGroup = appPermissionGroup;
+                    mAttributionTags = new HashSet<>();
+                    mDiscreteAccessTime = new ArrayList<>();
+                }
+
+                @NonNull Builder addAttributionTag(String attributionTag) {
+                    mAttributionTags.add(attributionTag);
+                    return this;
+                }
+
+                @NonNull
+                Builder addDiscreteAccessTime(
+                        Triple<Long, Long, OpEventProxyInfo> discreteAccessTime) {
+                    mDiscreteAccessTime.add(discreteAccessTime);
+                    return this;
+                }
+
+                AttributionLabelledGroupUsage build() {
+                    return new AttributionLabelledGroupUsage(mLabel,
+                            mAppPermissionGroup,
+                            new ArrayList<String>() {{
+                                addAll(mAttributionTags);
+                            }}, mDiscreteAccessTime);
+                }
+            }
+        }
     }
 
     public static class Builder {
@@ -400,4 +543,36 @@
                     mAudioRecordingConfigurations);
         }
     }
+
+    /** Usage for showing timeline view for a specific permission group with a label. */
+    public interface TimelineUsage {
+        /**
+         * Returns whether the usage has discrete data.
+         */
+        boolean hasDiscreteData();
+
+        /**
+         * Returns all discrete access time in millis.
+         * Returns a list of triples of (access time, access duration, proxy)
+         */
+        List<Triple<Long, Long, OpEventProxyInfo>> getAllDiscreteAccessTime();
+
+        /**
+         * Returns attribution tags for the usage.
+         */
+        List<String> getAttributionTags();
+
+        /**
+         * Returns the permission group of the usage.
+         */
+        AppPermissionGroup getGroup();
+
+        /**
+         * Returns the user facing string's resource id.
+         *
+         * <p> -1 means show the app name otherwise get the string resource from the app
+         * context.</p>
+         */
+        int getLabel();
+    }
 }
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/HibernationSettingState.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/HibernationSettingState.kt
index 636f199..69bbdce 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/HibernationSettingState.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/HibernationSettingState.kt
@@ -25,12 +25,9 @@
  * @param revocableGroupNames A list of which permission groups of this package are eligible for
  * auto-revoke. A permission group is auto-revocable if it does not contain a default granted
  * permission.
- * @param shouldAllowUserToggle If the hibernation/auto-revoke switch should be provided for the
- * user to control.
  */
 data class HibernationSettingState(
     val isEnabledGlobal: Boolean,
     val isEnabledForApp: Boolean,
-    val revocableGroupNames: List<String>,
-    val shouldAllowUserToggle: Boolean
+    val revocableGroupNames: List<String>
 )
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java
index f2466a4..dfcb39c 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java
@@ -551,7 +551,7 @@
         Preference autoRevokeSummary = autoRevokeCategory.findPreference(
                 AUTO_REVOKE_SUMMARY_KEY);
 
-        if (!state.isEnabledGlobal()) {
+        if (!state.isEnabledGlobal() || state.getRevocableGroupNames().isEmpty()) {
             autoRevokeCategory.setVisible(false);
             autoRevokeSwitch.setVisible(false);
             autoRevokeSummary.setVisible(false);
@@ -560,7 +560,6 @@
         autoRevokeCategory.setVisible(true);
         autoRevokeSwitch.setVisible(true);
         autoRevokeSummary.setVisible(true);
-        autoRevokeSwitch.setEnabled(state.getShouldAllowUserToggle());
         autoRevokeSwitch.setChecked(state.isEnabledForApp());
 
         List<String> groupLabels = new ArrayList<>();
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionsFragment.java
index 92f595e..2cd2773 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionsFragment.java
@@ -418,12 +418,11 @@
         if (state == null || autoRevokeSwitch == null) {
             return;
         }
-        if (!state.isEnabledGlobal()) {
+        if (!state.isEnabledGlobal() || state.getRevocableGroupNames().isEmpty()) {
             autoRevokeSwitch.setVisible(false);
             return;
         }
         autoRevokeSwitch.setVisible(true);
-        autoRevokeSwitch.setEnabled(state.getShouldAllowUserToggle());
         autoRevokeSwitch.setChecked(state.isEnabledForApp());
     }
 
diff --git a/service/java/com/android/role/RoleService.java b/service/java/com/android/role/RoleService.java
index 5df76c4..802e38b 100644
--- a/service/java/com/android/role/RoleService.java
+++ b/service/java/com/android/role/RoleService.java
@@ -704,6 +704,12 @@
 
         @Override
         public String getSmsRoleHolder(int userId) {
+            enforceCrossUserPermission(userId, false, "getSmsRoleHolder");
+            if (!isUserExistent(userId)) {
+                Log.e(LOG_TAG, "user " + userId + " does not exist");
+                return null;
+            }
+
             final long identity = Binder.clearCallingIdentity();
             try {
                 return CollectionUtils.firstOrNull(getRoleHoldersAsUser(RoleManager.ROLE_SMS,