1 /*
2  * Copyright (C) 2016 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.server.pm;
18 
19 import android.annotation.Nullable;
20 import android.annotation.UserIdInt;
21 import android.content.Context;
22 import android.os.UserHandle;
23 import android.util.ArraySet;
24 import android.util.SparseArray;
25 
26 import com.android.internal.R;
27 import com.android.internal.annotations.GuardedBy;
28 
29 import java.util.List;
30 import java.util.Set;
31 
32 /**
33  * Manages package names that need special protection.
34  *
35  * TODO: This class should persist the information by itself, and also keeps track of device admin
36  * packages for all users.  Then PMS.isPackageDeviceAdmin() should use it instead of talking
37  * to DPMS.
38  */
39 public class ProtectedPackages {
40     @UserIdInt
41     @GuardedBy("this")
42     private int mDeviceOwnerUserId;
43 
44     @Nullable
45     @GuardedBy("this")
46     private String mDeviceOwnerPackage;
47 
48     @Nullable
49     @GuardedBy("this")
50     private SparseArray<String> mProfileOwnerPackages;
51 
52     @Nullable
53     @GuardedBy("this")
54     private final String mDeviceProvisioningPackage;
55 
56     @Nullable
57     @GuardedBy("this")
58     private final SparseArray<Set<String>> mOwnerProtectedPackages = new SparseArray<>();
59 
60     private final Context mContext;
61 
ProtectedPackages(Context context)62     public ProtectedPackages(Context context) {
63         mContext = context;
64         mDeviceProvisioningPackage = mContext.getResources().getString(
65                 R.string.config_deviceProvisioningPackage);
66     }
67 
68     /**
69      * Sets the device/profile owner information.
70      */
setDeviceAndProfileOwnerPackages( int deviceOwnerUserId, String deviceOwnerPackage, SparseArray<String> profileOwnerPackages)71     public synchronized void setDeviceAndProfileOwnerPackages(
72             int deviceOwnerUserId, String deviceOwnerPackage,
73             SparseArray<String> profileOwnerPackages) {
74         mDeviceOwnerUserId = deviceOwnerUserId;
75         mDeviceOwnerPackage =
76                 (deviceOwnerUserId == UserHandle.USER_NULL) ? null : deviceOwnerPackage;
77         mProfileOwnerPackages = (profileOwnerPackages == null) ? null
78                 : profileOwnerPackages.clone();
79     }
80 
81     /** Sets packages protected by a device or profile owner. */
setOwnerProtectedPackages( @serIdInt int userId, @Nullable List<String> packageNames)82     public synchronized void setOwnerProtectedPackages(
83             @UserIdInt int userId, @Nullable List<String> packageNames) {
84         if (packageNames == null) {
85             mOwnerProtectedPackages.remove(userId);
86         } else {
87             mOwnerProtectedPackages.put(userId, new ArraySet<>(packageNames));
88         }
89     }
90 
hasDeviceOwnerOrProfileOwner(int userId, String packageName)91     private synchronized boolean hasDeviceOwnerOrProfileOwner(int userId, String packageName) {
92         if (packageName == null) {
93             return false;
94         }
95         if (mDeviceOwnerPackage != null) {
96             if ((mDeviceOwnerUserId == userId)
97                     && (packageName.equals(mDeviceOwnerPackage))) {
98                 return true;
99             }
100         }
101         if (mProfileOwnerPackages != null) {
102             if (packageName.equals(mProfileOwnerPackages.get(userId))) {
103                 return true;
104             }
105         }
106         return false;
107     }
108 
getDeviceOwnerOrProfileOwnerPackage(int userId)109     public synchronized String getDeviceOwnerOrProfileOwnerPackage(int userId) {
110         if (mDeviceOwnerUserId == userId) {
111             return mDeviceOwnerPackage;
112         }
113         if (mProfileOwnerPackages == null) {
114             return null;
115         }
116         return mProfileOwnerPackages.get(userId);
117     }
118 
119     /**
120      * Returns {@code true} if a given package is protected. Otherwise, returns {@code false}.
121      *
122      * <p>A protected package means that, apart from the package owner, no system or privileged apps
123      * can modify its data or package state.
124      */
isProtectedPackage(@serIdInt int userId, String packageName)125     private synchronized boolean isProtectedPackage(@UserIdInt int userId, String packageName) {
126         return packageName != null && (packageName.equals(mDeviceProvisioningPackage)
127                 || isOwnerProtectedPackage(userId, packageName));
128     }
129 
130     /**
131      * Returns {@code true} if the given package is a protected package set by any device or
132      * profile owner.
133      */
isOwnerProtectedPackage( @serIdInt int userId, String packageName)134     private synchronized boolean isOwnerProtectedPackage(
135             @UserIdInt int userId, String packageName) {
136         return hasProtectedPackages(userId)
137                 ? isPackageProtectedForUser(userId, packageName)
138                 : isPackageProtectedForUser(UserHandle.USER_ALL, packageName);
139     }
140 
isPackageProtectedForUser( @serIdInt int userId, String packageName)141     private synchronized boolean isPackageProtectedForUser(
142             @UserIdInt int userId, String packageName) {
143         int userIdx = mOwnerProtectedPackages.indexOfKey(userId);
144         return userIdx >= 0 && mOwnerProtectedPackages.valueAt(userIdx).contains(packageName);
145     }
146 
hasProtectedPackages(@serIdInt int userId)147     private synchronized boolean hasProtectedPackages(@UserIdInt int userId) {
148         return mOwnerProtectedPackages.indexOfKey(userId) >= 0;
149     }
150 
151     /**
152      * Returns {@code true} if a given package's state is protected. Otherwise, returns
153      * {@code false}.
154      *
155      * <p>This is not applicable if the caller is the package owner.
156      */
isPackageStateProtected(@serIdInt int userId, String packageName)157     public boolean isPackageStateProtected(@UserIdInt int userId, String packageName) {
158         return hasDeviceOwnerOrProfileOwner(userId, packageName)
159                 || isProtectedPackage(userId, packageName);
160     }
161 
162     /**
163      * Returns {@code true} if a given package's data is protected. Otherwise, returns
164      * {@code false}.
165      */
isPackageDataProtected(@serIdInt int userId, String packageName)166     public boolean isPackageDataProtected(@UserIdInt int userId, String packageName) {
167         return hasDeviceOwnerOrProfileOwner(userId, packageName)
168                 || isProtectedPackage(userId, packageName);
169     }
170 }
171