• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 android.app.admin;
18 
19 import android.annotation.BroadcastBehavior;
20 import android.annotation.NonNull;
21 import android.annotation.SdkConstant;
22 import android.annotation.TestApi;
23 import android.app.admin.flags.Flags;
24 import android.content.BroadcastReceiver;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.os.Bundle;
28 import android.text.TextUtils;
29 import android.util.Log;
30 
31 import java.util.Objects;
32 
33 /**
34  * Base class for implementing a policy update receiver. This class provides a convenience for
35  * interpreting the raw intent actions ({@link #ACTION_DEVICE_POLICY_SET_RESULT} and
36  * {@link #ACTION_DEVICE_POLICY_CHANGED}) that are sent by the system.
37  *
38  * <p>The callback methods happen on the main thread of the process. Thus, long-running
39  * operations must be done on another thread.
40  *
41  * <p>When publishing your {@code PolicyUpdateReceiver} subclass as a receiver, it must
42  * require the {@link android.Manifest.permission#BIND_DEVICE_ADMIN} permission.
43  *
44  * <p>Admins can implement {@link DeviceAdminService} to ensure they receive all policy updates
45  * (for policies they have set) via {@link #onPolicyChanged} by constantly being bound to by the
46  * system. For more information see {@link DeviceAdminService}.
47  */
48 public abstract class PolicyUpdateReceiver extends BroadcastReceiver {
49     private static String TAG = "PolicyUpdateReceiver";
50 
51     //TODO(b/378931989): Switch to android.app.admin.DevicePolicyIdentifiers.MEMORY_TAGGING_POLICY
52     //when the appropriate flag is launched.
53     private static final String MEMORY_TAGGING_POLICY = "memoryTagging";
54 
55     /**
56      * Action for a broadcast sent to admins to communicate back the result of setting a policy in
57      * {@link DevicePolicyManager}.
58      *
59      * <p>Admins wishing to receive these updates (via {@link #onPolicySetResult}) should include
60      * this action in the intent filter for their receiver in the manifest, the receiver
61      * must be protected by {@link android.Manifest.permission#BIND_DEVICE_ADMIN} to ensure that
62      * only the system can send updates.
63      *
64      * <p>Admins shouldn't implement {@link #onReceive} and should instead implement
65      * {@link #onPolicySetResult}.
66      */
67     @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
68     @BroadcastBehavior(explicitOnly = true)
69     public static final String ACTION_DEVICE_POLICY_SET_RESULT =
70             "android.app.admin.action.DEVICE_POLICY_SET_RESULT";
71 
72     /**
73      * Action for a broadcast sent to admins to communicate back a change in a policy they have
74      * previously set.
75      *
76      * <p>Admins wishing to receive these updates should include this action in the intent filter
77      * for their receiver in the manifest, the receiver must be protected by
78      * {@link android.Manifest.permission#BIND_DEVICE_ADMIN} to ensure that only the system can
79      * send updates.
80      *
81      * <p>Admins shouldn't implement {@link #onReceive} and should instead implement
82      * {@link #onPolicyChanged}.
83      */
84     @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
85     @BroadcastBehavior(explicitOnly = true)
86     public static final String ACTION_DEVICE_POLICY_CHANGED =
87             "android.app.admin.action.DEVICE_POLICY_CHANGED";
88 
89     /**
90      * A string extra holding the package name the policy applies to, (see
91      * {@link PolicyUpdateReceiver#onPolicyChanged} and
92      * {@link PolicyUpdateReceiver#onPolicySetResult})
93      */
94     public static final String EXTRA_PACKAGE_NAME =
95             "android.app.admin.extra.PACKAGE_NAME";
96 
97     /**
98      * A string extra holding the permission name the policy applies to, (see
99      * {@link PolicyUpdateReceiver#onPolicyChanged} and
100      * {@link PolicyUpdateReceiver#onPolicySetResult})
101      */
102     public static final String EXTRA_PERMISSION_NAME =
103             "android.app.admin.extra.PERMISSION_NAME";
104 
105     /**
106      * An {@link android.content.IntentFilter} extra holding the intent filter the policy relates
107      * to, (see {@link PolicyUpdateReceiver#onPolicyChanged} and
108      * {@link PolicyUpdateReceiver#onPolicySetResult})
109      */
110     public static final String EXTRA_INTENT_FILTER =
111             "android.app.admin.extra.INTENT_FILTER";
112 
113     /**
114      * A string extra holding the account type this policy applies to, (see
115      * {@link PolicyUpdateReceiver#onPolicyChanged} and
116      * {@link PolicyUpdateReceiver#onPolicySetResult})
117      */
118     public static final String EXTRA_ACCOUNT_TYPE =
119             "android.app.admin.extra.ACCOUNT_TYPE";
120 
121     /**
122      * String extra containing the policy identifier.
123      *
124      * @hide
125      */
126     @TestApi
127     public static final String EXTRA_POLICY_KEY = "android.app.admin.extra.POLICY_KEY";
128 
129     /**
130      * Bundle extra containing additional information related to a policy.
131      *
132      * @hide
133      */
134     @TestApi
135     public static final String EXTRA_POLICY_BUNDLE_KEY =
136             "android.app.admin.extra.POLICY_BUNDLE_KEY";
137 
138     /**
139      * Int extra containing the {@link PolicyUpdateResult} code.
140      *
141      * @hide
142      */
143     @TestApi
144     public static final String EXTRA_POLICY_UPDATE_RESULT_KEY =
145             "android.app.admin.extra.POLICY_UPDATE_RESULT_KEY";
146 
147     /**
148      * Int extra containing the target user this policy update applies to.
149      *
150      * @hide
151      */
152     @TestApi
153     public static final String EXTRA_POLICY_TARGET_USER_ID =
154             "android.app.admin.extra.POLICY_TARGET_USER_ID";
155 
156     /**
157      * Intercept standard policy update broadcasts. Implementations should not override this
158      * method and rely on the callbacks instead.
159      *
160      * @hide
161      */
162     @Override
onReceive(Context context, Intent intent)163     public final void onReceive(Context context, Intent intent) {
164         Objects.requireNonNull(intent.getAction());
165         String policyKey;
166         switch (intent.getAction()) {
167             case ACTION_DEVICE_POLICY_SET_RESULT:
168                 Log.i(TAG, "Received ACTION_DEVICE_POLICY_SET_RESULT");
169                 policyKey = getPolicyKey(intent);
170                 if (!shouldPropagatePolicy(policyKey)) {
171                     Log.d(TAG, TextUtils.formatSimple(
172                             "Skipping propagation of policy %s", policyKey));
173                     break;
174                 }
175                 onPolicySetResult(context, policyKey, getPolicyExtraBundle(intent),
176                         getTargetUser(intent), getPolicyChangedReason(intent));
177                 break;
178             case ACTION_DEVICE_POLICY_CHANGED:
179                 Log.i(TAG, "Received ACTION_DEVICE_POLICY_CHANGED");
180                 policyKey = getPolicyKey(intent);
181                 if (!shouldPropagatePolicy(policyKey)) {
182                     Log.d(TAG, TextUtils.formatSimple(
183                             "Skipping propagation of policy %s", policyKey));
184                     break;
185                 }
186                 onPolicyChanged(context, policyKey, getPolicyExtraBundle(intent),
187                         getTargetUser(intent), getPolicyChangedReason(intent));
188                 break;
189             default:
190                 Log.e(TAG, "Unknown action received: " + intent.getAction());
191         }
192     }
193 
194     /**
195      * @hide
196      */
getPolicyKey(Intent intent)197     static String getPolicyKey(Intent intent) {
198         if (!intent.hasExtra(EXTRA_POLICY_KEY)) {
199             throw new IllegalArgumentException("PolicyKey has to be provided.");
200         }
201         return intent.getStringExtra(EXTRA_POLICY_KEY);
202     }
203 
204     /**
205      * @hide
206      */
207     @NonNull
getPolicyExtraBundle(Intent intent)208     static Bundle getPolicyExtraBundle(Intent intent) {
209         Bundle bundle = intent.getBundleExtra(EXTRA_POLICY_BUNDLE_KEY);
210         return bundle == null ? new Bundle() : bundle;
211     }
212 
213     /**
214      * @hide
215      */
216     @NonNull
getPolicyChangedReason(Intent intent)217     static PolicyUpdateResult getPolicyChangedReason(Intent intent) {
218         if (!intent.hasExtra(EXTRA_POLICY_UPDATE_RESULT_KEY)) {
219             throw new IllegalArgumentException("PolicyUpdateResult has to be provided.");
220         }
221         int reasonCode = intent.getIntExtra(
222                 EXTRA_POLICY_UPDATE_RESULT_KEY, PolicyUpdateResult.RESULT_FAILURE_UNKNOWN);
223         return new PolicyUpdateResult(reasonCode);
224     }
225 
226     /**
227      * @hide
228      */
229     @NonNull
getTargetUser(Intent intent)230     static TargetUser getTargetUser(Intent intent) {
231         if (!intent.hasExtra(EXTRA_POLICY_TARGET_USER_ID)) {
232             throw new IllegalArgumentException("TargetUser has to be provided.");
233         }
234         int targetUserId = intent.getIntExtra(
235                 EXTRA_POLICY_TARGET_USER_ID, TargetUser.LOCAL_USER_ID);
236         return new TargetUser(targetUserId);
237     }
238 
239     /**
240      * @hide
241      */
shouldPropagatePolicy(String policyKey)242     private boolean shouldPropagatePolicy(String policyKey) {
243         return !MEMORY_TAGGING_POLICY.equals(policyKey) || Flags.setMtePolicyCoexistence();
244     }
245 
246 
247     // TODO(b/260847505): Add javadocs to explain which DPM APIs are supported
248     /**
249      * Callback triggered after an admin has set a policy using one of the APIs in
250      * {@link DevicePolicyManager} to notify the admin whether it has been successful or not.
251      *
252      * <p>Admins wishing to receive this callback should include
253      * {@link PolicyUpdateReceiver#ACTION_DEVICE_POLICY_SET_RESULT} in the intent filter for their
254      * receiver in the manifest, the receiver must be protected by
255      * {@link android.Manifest.permission#BIND_DEVICE_ADMIN} to ensure that only the system can
256      * send updates.
257      *
258      * @param context the running context as per {@link #onReceive}
259      * @param policyIdentifier Key to identify which policy this callback relates to.
260      * @param additionalPolicyParams Bundle containing additional params that may be required to
261      *                               identify some of the policy
262      *                               (e.g. {@link PolicyUpdateReceiver#EXTRA_PACKAGE_NAME}
263      *                               and {@link PolicyUpdateReceiver#EXTRA_PERMISSION_NAME}).
264      *                               Each policy will document the required additional params if
265      *                               needed.
266      * @param targetUser The {@link TargetUser} which this policy relates to.
267      * @param policyUpdateResult Indicates whether the policy has been set successfully
268      *                           ({@link PolicyUpdateResult#RESULT_POLICY_SET}) or the reason it
269      *                           failed to apply (e.g.
270      *                           {@link PolicyUpdateResult#RESULT_FAILURE_CONFLICTING_ADMIN_POLICY},
271      *                           etc).
272      */
onPolicySetResult( @onNull Context context, @NonNull String policyIdentifier, @NonNull Bundle additionalPolicyParams, @NonNull TargetUser targetUser, @NonNull PolicyUpdateResult policyUpdateResult)273     public void onPolicySetResult(
274             @NonNull Context context,
275             @NonNull String policyIdentifier,
276             @NonNull Bundle additionalPolicyParams,
277             @NonNull TargetUser targetUser,
278             @NonNull PolicyUpdateResult policyUpdateResult) {}
279 
280     // TODO(b/260847505): Add javadocs to explain which DPM APIs are supported
281     // TODO(b/261430877): Add javadocs to explain when will this get triggered
282     /**
283      * Callback triggered when a policy previously set by the admin has changed.
284      *
285      * <p>Admins wishing to receive this callback should include
286      * {@link PolicyUpdateReceiver#ACTION_DEVICE_POLICY_CHANGED} in the intent filter for their
287      * receiver in the manifest, the receiver must be protected by
288      * {@link android.Manifest.permission#BIND_DEVICE_ADMIN} to ensure that only the system can
289      * send updates.
290      *
291      * @param context the running context as per {@link #onReceive}
292      * @param policyIdentifier Key to identify which policy this callback relates to.
293      * @param additionalPolicyParams Bundle containing additional params that may be required to
294      *                               identify some of the policy
295      *                               (e.g. {@link PolicyUpdateReceiver#EXTRA_PACKAGE_NAME}
296      *                               and {@link PolicyUpdateReceiver#EXTRA_PERMISSION_NAME}).
297      *                               Each policy will document the required additional params if
298      *                               needed.
299      * @param targetUser The {@link TargetUser} which this policy relates to.
300      * @param policyUpdateResult Indicates the reason the policy value has changed
301      *                           (e.g. {@link PolicyUpdateResult#RESULT_POLICY_SET} if the policy
302      *                           has changed to the value set by the admin,
303      *                           {@link PolicyUpdateResult#RESULT_FAILURE_CONFLICTING_ADMIN_POLICY}
304      *                           if the policy has changed because another admin has set a
305      *                           conflicting policy, etc)
306      */
onPolicyChanged( @onNull Context context, @NonNull String policyIdentifier, @NonNull Bundle additionalPolicyParams, @NonNull TargetUser targetUser, @NonNull PolicyUpdateResult policyUpdateResult)307     public void onPolicyChanged(
308             @NonNull Context context,
309             @NonNull String policyIdentifier,
310             @NonNull Bundle additionalPolicyParams,
311             @NonNull TargetUser targetUser,
312             @NonNull PolicyUpdateResult policyUpdateResult) {}
313 }
314