1 /*
2  * Copyright (C) 2019 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.packageinstaller.permission.data;
18 
19 import static com.android.packageinstaller.permission.utils.Utils.FLAGS_ALWAYS_USER_SENSITIVE;
20 
21 import android.app.Application;
22 import android.content.BroadcastReceiver;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.content.pm.ApplicationInfo;
27 import android.content.pm.PackageInfo;
28 import android.content.pm.PackageManager;
29 import android.os.Process;
30 import android.os.UserHandle;
31 import android.util.ArrayMap;
32 import android.util.ArraySet;
33 import android.util.SparseArray;
34 
35 import androidx.annotation.MainThread;
36 import androidx.annotation.NonNull;
37 
38 import com.android.packageinstaller.AsyncTaskLiveData;
39 import com.android.packageinstaller.permission.utils.ArrayUtils;
40 import com.android.packageinstaller.permission.utils.Utils;
41 
42 import java.util.List;
43 import java.util.Set;
44 
45 /**
46  * Live data of the user sensitivity of all uids that belong to a given user
47  *
48  * <p>Data source: system server
49  */
50 public class PerUserUidToSensitivityLiveData extends
51         AsyncTaskLiveData<SparseArray<ArrayMap<String, Integer>>> {
52     private static final SparseArray<PerUserUidToSensitivityLiveData> sInstances =
53             new SparseArray<>();
54 
55     private final Context mContext;
56     private final UserHandle mUser;
57 
58     /** Monitors changes to the packages for a user */
59     private final BroadcastReceiver mPackageMonitor = new BroadcastReceiver() {
60         @Override
61         public void onReceive(Context context, Intent intent) {
62             loadValue();
63         }
64     };
65 
66     /**
67      * Get a (potentially shared) live data.
68      *
69      * @param user The user to get the data for
70      * @param application The application context
71      *
72      * @return The live data
73      */
74     @MainThread
get(@onNull UserHandle user, @NonNull Application application)75     public static PerUserUidToSensitivityLiveData get(@NonNull UserHandle user,
76             @NonNull Application application) {
77         PerUserUidToSensitivityLiveData instance = sInstances.get(user.getIdentifier());
78         if (instance == null) {
79             instance = new PerUserUidToSensitivityLiveData(user, application);
80             sInstances.put(user.getIdentifier(), instance);
81         }
82 
83         return instance;
84     }
85 
PerUserUidToSensitivityLiveData(@onNull UserHandle user, @NonNull Application application)86     private PerUserUidToSensitivityLiveData(@NonNull UserHandle user,
87             @NonNull Application application) {
88         mUser = user;
89 
90         try {
91             mContext = application.createPackageContextAsUser(application.getPackageName(), 0,
92                     user);
93         } catch (PackageManager.NameNotFoundException cannotHappen) {
94             throw new IllegalStateException(cannotHappen);
95         }
96     }
97 
98     @Override
onActive()99     protected void onActive() {
100         loadValue();
101         mContext.registerReceiver(mPackageMonitor, new IntentFilter(Intent.ACTION_PACKAGE_CHANGED));
102     }
103 
104     @Override
onInactive()105     protected void onInactive() {
106         mContext.unregisterReceiver(mPackageMonitor);
107     }
108 
109     @Override
loadValueInBackground()110     public SparseArray<ArrayMap<String, Integer>> loadValueInBackground() {
111         PackageManager pm = mContext.getPackageManager();
112         List<PackageInfo> pkgs = pm.getInstalledPackages(PackageManager.GET_PERMISSIONS);
113         Set<String> platformPerms = Utils.getPlatformPermissions();
114         ArraySet<String> pkgsWithLauncherIcon = Utils.getLauncherPackages(mContext);
115 
116         // uid -> permission -> flags
117         SparseArray<ArrayMap<String, Integer>> uidsPermissions = new SparseArray<>();
118 
119         // Collect the flags and store it in 'uidsPermissions'
120         int numPkgs = pkgs.size();
121         for (int pkgNum = 0; pkgNum < numPkgs; pkgNum++) {
122             PackageInfo pkg = pkgs.get(pkgNum);
123             boolean pkgHasLauncherIcon = pkgsWithLauncherIcon.contains(pkg.packageName);
124             boolean pkgIsSystemApp = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
125 
126             // permission -> flags
127             ArrayMap<String, Integer> uidPermissions = uidsPermissions.get(pkg.applicationInfo.uid);
128             if (uidPermissions == null) {
129                 uidPermissions = new ArrayMap<>();
130                 uidsPermissions.put(pkg.applicationInfo.uid, uidPermissions);
131             }
132 
133             for (String perm : platformPerms) {
134                 if (!ArrayUtils.contains(pkg.requestedPermissions, perm)) {
135                     continue;
136                 }
137 
138                 /*
139                  * Permissions are considered user sensitive for a package, when
140                  * - the package has a launcher icon, or
141                  * - the permission is not pre-granted, or
142                  * - the package is not a system app (i.e. not preinstalled)
143                  *
144                  * If two packages share a UID there can be two cases:
145                  * - for well known UIDs: if the permission for any package is non-user sensitive,
146                  *                        it is non-sensitive. I.e. prefer to hide
147                  * - for non system UIDs: if the permission for any package is user sensitive, it is
148                  *                        user sensitive. I.e. prefer to show
149                  */
150                 Integer previousFlagsInt = uidPermissions.get(perm);
151                 int previousFlags;
152                 if (pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID) {
153                     previousFlags = previousFlagsInt == null
154                             ? FLAGS_ALWAYS_USER_SENSITIVE
155                             : previousFlagsInt;
156                 } else {
157                     previousFlags = previousFlagsInt == null ? 0 : previousFlagsInt;
158                 }
159 
160                 int flags;
161                 if (pkgIsSystemApp && !pkgHasLauncherIcon) {
162                     boolean permGrantedByDefault = (pm.getPermissionFlags(perm, pkg.packageName,
163                             mUser) & PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT) != 0;
164 
165                     if (permGrantedByDefault) {
166                         flags = 0;
167                     } else {
168                         flags = PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED;
169                     }
170                 } else {
171                     flags = FLAGS_ALWAYS_USER_SENSITIVE;
172                 }
173 
174                 if (pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID) {
175                     flags &= previousFlags;
176                 } else {
177                     flags |= previousFlags;
178                 }
179 
180                 uidPermissions.put(perm, flags);
181             }
182         }
183 
184         return uidsPermissions;
185     }
186 }
187