Skip to content

Commit 7080ac7

Browse files
committed
Application restrictions proxy code
Bug: 26111675 Change-Id: I4355c43178f9567ece9c83f195777c649359b4e4
1 parent ddbef0a commit 7080ac7

File tree

3 files changed

+306
-0
lines changed

3 files changed

+306
-0
lines changed

app/src/main/AndroidManifest.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,12 @@
117117
android:resource="@xml/filepaths" />
118118
</provider>
119119

120+
<service android:name=".profilepolicy.apprestrictions.AppRestrictionsProxy">
121+
<intent-filter>
122+
<action android:name="com.android.vending.dpc.APPLICATION_RESTRICTIONS_PROXY" />
123+
</intent-filter>
124+
</service>
125+
120126
</application>
121127

122128
</manifest>
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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.afwsamples.testdpc.profilepolicy.apprestrictions;
18+
19+
import android.app.Service;
20+
import android.app.admin.DevicePolicyManager;
21+
import android.content.ComponentName;
22+
import android.content.Intent;
23+
import android.os.Bundle;
24+
import android.os.IBinder;
25+
import android.os.Messenger;
26+
27+
import com.afwsamples.testdpc.DeviceAdminReceiver;
28+
29+
/**
30+
* Before N, only the DPC has permission to set application restrictions via
31+
* {@link DevicePolicyManager#setApplicationRestrictions(ComponentName, String, Bundle)}.
32+
*
33+
* To enable the another package to manage the application restrictions, a bound service is used to
34+
* pass them to {@link AppRestrictionsProxy}.
35+
*
36+
* From N onwards, a given package can be granted permission to manage application restrictions,
37+
* which removes the need for the proxy code.
38+
*/
39+
public class AppRestrictionsProxy extends Service {
40+
41+
private final Messenger mMessenger = new Messenger(new AppRestrictionsProxyHandler(this,
42+
DeviceAdminReceiver.getComponentName(this)));
43+
44+
@Override
45+
public IBinder onBind(Intent intent) {
46+
return mMessenger.getBinder();
47+
}
48+
}
Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
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.afwsamples.testdpc.profilepolicy.apprestrictions;
18+
19+
import android.app.admin.DevicePolicyManager;
20+
import android.content.ComponentName;
21+
import android.content.Context;
22+
import android.content.pm.PackageInfo;
23+
import android.content.pm.PackageManager;
24+
import android.content.pm.PackageManager.NameNotFoundException;
25+
import android.content.pm.Signature;
26+
import android.os.Bundle;
27+
import android.os.Handler;
28+
import android.os.Message;
29+
import android.os.RemoteException;
30+
import android.preference.PreferenceManager;
31+
import android.util.Log;
32+
33+
import java.util.ArrayList;
34+
import java.util.HashSet;
35+
import java.util.List;
36+
import java.util.Set;
37+
38+
/**
39+
* Handles the message passed from another package to set the application restrictions.
40+
*
41+
* The package name must be provided, along with the application restrictions bundle to set.
42+
* To clear the application restrictions, an empty bundle should be passed.
43+
*/
44+
public class AppRestrictionsProxyHandler extends Handler {
45+
46+
private static final String TAG = "AppRestrictionsProxy";
47+
private static final int MSG_SET_APPLICATION_RESTRICTIONS = 1;
48+
private static final int MSG_CAN_SET_APPLICATION_RESTRICTIONS = 2;
49+
private static final int MSG_GET_APPLICATION_RESTRICTIONS = 3;
50+
51+
private static final String APPLICATION_RESTRICTIONS_MANAGING_PACKAGE_SIGNATURES_KEY =
52+
"application_restrictions_managing_package_signatures";
53+
private static final String APPLICATION_RESTRICTIONS_MANAGING_PACKAGE_KEY =
54+
"application_restrictions_managing_package";
55+
56+
public static final String KEY_APPLICATION_RESTRICTIONS = "applicationRestrictions";
57+
public static final String KEY_PACKAGE_NAME = "packageName";
58+
public static final String KEY_CAN_SET_APPLICATION_RESTRICTIONS
59+
= "canSetApplicationRestrictions";
60+
61+
private final Context mContext;
62+
private final ComponentName mAdmin;
63+
64+
public AppRestrictionsProxyHandler(Context context, ComponentName admin) {
65+
mContext = context;
66+
mAdmin = admin;
67+
}
68+
69+
@Override
70+
public void handleMessage(Message msg) {
71+
switch (msg.what) {
72+
case MSG_SET_APPLICATION_RESTRICTIONS: {
73+
ensureCallerSignature(msg.sendingUid);
74+
String packageName = msg.getData().getString(KEY_PACKAGE_NAME);
75+
Bundle appRestrictions = msg.getData().getBundle(KEY_APPLICATION_RESTRICTIONS);
76+
setApplicationRestrictions(packageName, appRestrictions);
77+
break;
78+
}
79+
case MSG_CAN_SET_APPLICATION_RESTRICTIONS: {
80+
String callingPackage = mContext.getPackageManager().getNameForUid(msg.sendingUid);
81+
String managingPackage = getApplicationRestrictionsManagingPackage();
82+
Bundle responseBundle = new Bundle();
83+
responseBundle.putBoolean(KEY_CAN_SET_APPLICATION_RESTRICTIONS,
84+
callingPackage != null && callingPackage.equals(managingPackage));
85+
Message response = Message.obtain();
86+
response.setData(responseBundle);
87+
try {
88+
msg.replyTo.send(response);
89+
} catch (RemoteException e) {
90+
Log.e(TAG, "Unable to respond to canSetApplicationRestrictions.", e);
91+
}
92+
break;
93+
}
94+
case MSG_GET_APPLICATION_RESTRICTIONS: {
95+
ensureCallerSignature(msg.sendingUid);
96+
String packageName = msg.getData().getString(KEY_PACKAGE_NAME);
97+
Bundle appRestrictions = getApplicationRestrictions(packageName);
98+
Bundle responseBundle = new Bundle();
99+
responseBundle.putBundle(KEY_APPLICATION_RESTRICTIONS, appRestrictions);
100+
Message response = Message.obtain();
101+
response.setData(responseBundle);
102+
try {
103+
msg.replyTo.send(response);
104+
} catch (RemoteException e) {
105+
Log.e(TAG, "Unable to respond to getApplicationRestrictions.", e);
106+
}
107+
break;
108+
}
109+
default:
110+
throw new IllegalArgumentException("Unknown 'what': " + msg.what);
111+
}
112+
}
113+
114+
/**
115+
* Called by a profile owner or device owner to grant permission to a package to manage
116+
* application restrictions for the calling user via the {@link AppRestrictionsProxy}.
117+
*
118+
* This permission is persistent until it is later cleared by calling this method with a
119+
* {@code null} value.
120+
*
121+
* The supplied application restriction managing package must be installed when calling this
122+
* API, otherwise an {@link IllegalArgumentException} will be thrown.
123+
*/
124+
public void setApplicationRestrictionsManagingPackage(String packageName) {
125+
if (packageName == null) {
126+
PreferenceManager.getDefaultSharedPreferences(mContext).edit()
127+
.putStringSet(APPLICATION_RESTRICTIONS_MANAGING_PACKAGE_SIGNATURES_KEY, null);
128+
PreferenceManager.getDefaultSharedPreferences(mContext).edit()
129+
.putString(APPLICATION_RESTRICTIONS_MANAGING_PACKAGE_KEY, null);
130+
return;
131+
}
132+
Signature[] signatures;
133+
try {
134+
PackageManager packageManager = mContext.getPackageManager();
135+
PackageInfo packageInfo =
136+
packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
137+
if (packageInfo == null) {
138+
throw new IllegalArgumentException("Package info could not be retrieved for " +
139+
"package " + packageName + ".");
140+
}
141+
signatures = packageInfo.signatures;
142+
if (signatures == null) {
143+
throw new IllegalArgumentException("Package info did not contain signatures " +
144+
"for package " + packageName + ".");
145+
}
146+
} catch (NameNotFoundException e) {
147+
throw new IllegalArgumentException("Cannot set " + packageName + " as application " +
148+
"restriction managing package as it is not installed.", e);
149+
}
150+
Set<String> signatureSet = new HashSet<>();
151+
for (Signature signature : signatures) {
152+
signatureSet.add(signature.toCharsString());
153+
}
154+
155+
PreferenceManager.getDefaultSharedPreferences(mContext).edit()
156+
.putStringSet(APPLICATION_RESTRICTIONS_MANAGING_PACKAGE_SIGNATURES_KEY,
157+
signatureSet);
158+
PreferenceManager.getDefaultSharedPreferences(mContext).edit()
159+
.putString(APPLICATION_RESTRICTIONS_MANAGING_PACKAGE_KEY, packageName);
160+
}
161+
162+
/**
163+
* Called by a profile owner or device owner to retrieve the application restrictions managing
164+
* package for the current user, or {@code null} if none is set.
165+
*/
166+
public String getApplicationRestrictionsManagingPackage(){
167+
return PreferenceManager.getDefaultSharedPreferences(mContext)
168+
.getString(APPLICATION_RESTRICTIONS_MANAGING_PACKAGE_KEY, null);
169+
}
170+
171+
private void setApplicationRestrictions(String packageName, Bundle appRestrictions){
172+
if (packageName == null) {
173+
throw new IllegalArgumentException("packageName cannot be null.");
174+
}
175+
if (appRestrictions == null) {
176+
throw new IllegalArgumentException("applicationRestrictions bundle " +
177+
"cannot be null.");
178+
}
179+
Log.d(TAG, "Setting application restrictions for package " + packageName);
180+
DevicePolicyManager devicePolicyManager = (DevicePolicyManager)
181+
mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
182+
devicePolicyManager.setApplicationRestrictions(mAdmin, packageName,
183+
appRestrictions);
184+
}
185+
186+
private Bundle getApplicationRestrictions(String packageName){
187+
if (packageName == null) {
188+
throw new IllegalArgumentException("packageName cannot be null.");
189+
}
190+
DevicePolicyManager devicePolicyManager = (DevicePolicyManager)
191+
mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
192+
return devicePolicyManager.getApplicationRestrictions(mAdmin, packageName);
193+
}
194+
195+
/**
196+
* Checks that the message sent through the bound service was sent by the same package as
197+
* declared in {@link #setApplicationRestrictionsManagingPackage(String)}, and
198+
* that its signature has not changed since it was set.
199+
*
200+
* @param callerUid the UID of the caller
201+
*
202+
* @throws SecurityException if the DPC hasn't given permission to the caller to manage
203+
* application restrictions, or if the calling package's signature has changed since it was
204+
* set.
205+
*/
206+
private void ensureCallerSignature(int callerUid) {
207+
String appRestrictionsManagingPackage = getApplicationRestrictionsManagingPackage();
208+
if (appRestrictionsManagingPackage == null) {
209+
throw new SecurityException("Caller is not app restrictions managing package");
210+
}
211+
PackageManager packageManager = mContext.getPackageManager();
212+
String callingPackageName = packageManager.getNameForUid(callerUid);
213+
if (!appRestrictionsManagingPackage.equals(callingPackageName)) {
214+
throw new SecurityException("Caller is not app restrictions managing package");
215+
}
216+
217+
Set<String> storedSignatures = PreferenceManager.getDefaultSharedPreferences(mContext)
218+
.getStringSet(APPLICATION_RESTRICTIONS_MANAGING_PACKAGE_SIGNATURES_KEY, null);
219+
if (storedSignatures == null) {
220+
throw new IllegalStateException(
221+
"App restrictions managing package signatures have not been stored.");
222+
}
223+
Signature[] callingPackageSignatures;
224+
try {
225+
PackageInfo packageInfo = packageManager
226+
.getPackageInfo(callingPackageName, PackageManager.GET_SIGNATURES);
227+
if (packageInfo == null) {
228+
throw new IllegalArgumentException("Package info could not be retrieved for " +
229+
"package " + callingPackageName + ".");
230+
}
231+
callingPackageSignatures = packageInfo.signatures;
232+
if (callingPackageSignatures == null) {
233+
throw new IllegalArgumentException("Package info did not contain signatures " +
234+
"for package " + callingPackageName + ".");
235+
}
236+
} catch (NameNotFoundException e) {
237+
throw new SecurityException(e);
238+
}
239+
List<Signature> expectedSignatures = new ArrayList<>(storedSignatures.size());
240+
for (String signatureString : storedSignatures) {
241+
expectedSignatures.add(new Signature(signatureString));
242+
}
243+
for (Signature callingSignature : callingPackageSignatures) {
244+
for (Signature expectedSignature : expectedSignatures) {
245+
if (expectedSignature.equals(callingSignature)) {
246+
return;
247+
}
248+
}
249+
}
250+
throw new SecurityException("Calling package signature doesn't match");
251+
}
252+
}

0 commit comments

Comments
 (0)