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