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