1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.permissioncontroller.permission.utils.v31;
18 
19 import android.content.Context;
20 import android.content.pm.ApplicationInfo;
21 import android.content.pm.Attribution;
22 import android.content.pm.PackageInfo;
23 import android.content.pm.PackageManager;
24 import android.content.res.Resources;
25 import android.os.Build;
26 
27 import androidx.annotation.ChecksSdkIntAtLeast;
28 import androidx.annotation.Nullable;
29 import androidx.annotation.RequiresApi;
30 
31 import com.android.modules.utils.build.SdkLevel;
32 import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo;
33 
34 import java.util.HashMap;
35 import java.util.Map;
36 
37 /**
38  * Utils related to subattribution.
39  */
40 public class SubattributionUtils {
41 
42     /**
43      * Returns true if the app supports subattribution.
44      */
45     @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.S)
isSubattributionSupported(Context context, ApplicationInfo appInfo)46     public static boolean isSubattributionSupported(Context context, ApplicationInfo appInfo) {
47         if (!SdkLevel.isAtLeastS()) {
48             return false;
49         }
50         return appInfo.areAttributionsUserVisible();
51     }
52 
53     /** Returns whether the provided package supports subattribution. */
54     @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.S)
isSubattributionSupported(LightPackageInfo lightPackageInfo)55     public static boolean isSubattributionSupported(LightPackageInfo lightPackageInfo) {
56         return SdkLevel.isAtLeastS() && lightPackageInfo.getAreAttributionsUserVisible();
57     }
58 
59     /**
60      * Returns the attribution label map for the package if the app supports subattribution; Returns
61      * {@code null} otherwise.
62      */
63     @Nullable
getAttributionLabels(Context context, PackageInfo pkgInfo)64     public static Map<Integer, String> getAttributionLabels(Context context, PackageInfo pkgInfo) {
65         if (!isSubattributionSupported(context, pkgInfo.applicationInfo)) {
66             return null;
67         }
68         return getAttributionLabelsInternal(context, pkgInfo);
69     }
70 
71     /**
72      * Returns the attribution label map for the package if the app supports subattribtuion; Returns
73      * {@code null} otherwise.
74      */
75     @Nullable
getAttributionLabels(Context context, ApplicationInfo appInfo)76     public static Map<Integer, String> getAttributionLabels(Context context,
77             ApplicationInfo appInfo) {
78         if (!isSubattributionSupported(context, appInfo)) {
79             return null;
80         }
81 
82         PackageInfo packageInfo;
83         try {
84             packageInfo = context.getPackageManager().getPackageInfo(appInfo.packageName,
85                     PackageManager.GET_PERMISSIONS | PackageManager.GET_ATTRIBUTIONS);
86         } catch (PackageManager.NameNotFoundException e) {
87             return null;
88         }
89         return getAttributionLabelsInternal(context, packageInfo);
90     }
91 
92     @Nullable
93     @RequiresApi(Build.VERSION_CODES.S)
getAttributionLabelsInternal(Context context, PackageInfo pkgInfo)94     private static Map<Integer, String> getAttributionLabelsInternal(Context context,
95             PackageInfo pkgInfo) {
96         Context pkgContext;
97         try {
98             pkgContext = context.createPackageContext(pkgInfo.packageName, 0);
99         } catch (PackageManager.NameNotFoundException e) {
100             return null;
101         }
102         Map<Integer, String> attributionLabels = new HashMap<>();
103         for (Attribution attribution : pkgInfo.attributions) {
104             int label = attribution.getLabel();
105             try {
106                 String resourceForLabel = pkgContext.getString(attribution.getLabel());
107                 attributionLabels.put(label, resourceForLabel);
108             } catch (Resources.NotFoundException e) {
109                 // should never happen
110             }
111         }
112         return attributionLabels;
113     }
114 
115     /** Returns the attribution label map for the package if the app supports subattribution. */
116     @Nullable
getAttributionLabels(Context context, LightPackageInfo lightPackageInfo)117     public static Map<Integer, String> getAttributionLabels(Context context,
118             LightPackageInfo lightPackageInfo) {
119         if (!isSubattributionSupported(lightPackageInfo)) {
120             return null;
121         }
122 
123         Context pkgContext;
124         try {
125             pkgContext = context.createPackageContext(lightPackageInfo.getPackageName(), 0);
126         } catch (PackageManager.NameNotFoundException e) {
127             return null;
128         }
129 
130         Map<Integer, String> attributionLabels = new HashMap<>();
131         for (Map.Entry<String, Integer> attribution :
132                 lightPackageInfo.getAttributionTagsToLabels().entrySet()) {
133             int label = attribution.getValue();
134             try {
135                 String resourceForLabel = pkgContext.getString(attribution.getValue());
136                 attributionLabels.put(label, resourceForLabel);
137             } catch (Resources.NotFoundException e) {
138                 // should never happen
139             }
140         }
141         return attributionLabels;
142     }
143 }
144