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