1 /* 2 * Copyright (C) 2019 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.service.controls.actions; 18 19 import android.annotation.CallSuper; 20 import android.annotation.IntDef; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.os.Bundle; 24 import android.service.controls.Control; 25 import android.service.controls.ControlsProviderService; 26 import android.service.controls.templates.ControlTemplate; 27 import android.util.Log; 28 29 import com.android.internal.util.Preconditions; 30 31 import java.lang.annotation.Retention; 32 import java.lang.annotation.RetentionPolicy; 33 34 /** 35 * An abstract action indicating a user interaction with a {@link Control}. 36 * 37 * In some cases, an action needs to be validated by the user, using a password, PIN or simple 38 * acknowledgment. For those cases, an optional (nullable) parameter can be passed to send the user 39 * input. This <b>challenge value</b> will be requested from the user and sent as part 40 * of a {@link ControlAction} only if the service has responded to an action with one of: 41 * <ul> 42 * <li> {@link #RESPONSE_CHALLENGE_ACK} 43 * <li> {@link #RESPONSE_CHALLENGE_PIN} 44 * <li> {@link #RESPONSE_CHALLENGE_PASSPHRASE} 45 * </ul> 46 */ 47 public abstract class ControlAction { 48 49 private static final String TAG = "ControlAction"; 50 51 private static final String KEY_ACTION_TYPE = "key_action_type"; 52 private static final String KEY_TEMPLATE_ID = "key_template_id"; 53 private static final String KEY_CHALLENGE_VALUE = "key_challenge_value"; 54 55 /** 56 * @hide 57 */ 58 @Retention(RetentionPolicy.SOURCE) 59 @IntDef({ 60 TYPE_ERROR, 61 TYPE_BOOLEAN, 62 TYPE_FLOAT, 63 TYPE_MODE, 64 TYPE_COMMAND 65 }) 66 public @interface ActionType {}; 67 68 /** 69 * Object returned when there is an unparcelling error. 70 * @hide 71 */ 72 public static final @NonNull ControlAction ERROR_ACTION = new ControlAction() { 73 @Override 74 public int getActionType() { 75 return TYPE_ERROR; 76 } 77 }; 78 79 /** 80 * The identifier of the action returned by {@link #getErrorAction}. 81 */ 82 public static final @ActionType int TYPE_ERROR = -1; 83 84 /** 85 * The identifier of {@link BooleanAction}. 86 */ 87 public static final @ActionType int TYPE_BOOLEAN = 1; 88 89 /** 90 * The identifier of {@link FloatAction}. 91 */ 92 public static final @ActionType int TYPE_FLOAT = 2; 93 94 /** 95 * The identifier of {@link ModeAction}. 96 */ 97 public static final @ActionType int TYPE_MODE = 4; 98 99 /** 100 * The identifier of {@link CommandAction}. 101 */ 102 public static final @ActionType int TYPE_COMMAND = 5; 103 104 isValidResponse(@esponseResult int response)105 public static final boolean isValidResponse(@ResponseResult int response) { 106 return (response >= 0 && response < NUM_RESPONSE_TYPES); 107 } 108 private static final int NUM_RESPONSE_TYPES = 6; 109 /** 110 * @hide 111 */ 112 @Retention(RetentionPolicy.SOURCE) 113 @IntDef({ 114 RESPONSE_UNKNOWN, 115 RESPONSE_OK, 116 RESPONSE_FAIL, 117 RESPONSE_CHALLENGE_ACK, 118 RESPONSE_CHALLENGE_PIN, 119 RESPONSE_CHALLENGE_PASSPHRASE 120 }) 121 public @interface ResponseResult {}; 122 123 public static final @ResponseResult int RESPONSE_UNKNOWN = 0; 124 125 /** 126 * Response code for the {@code consumer} in 127 * {@link ControlsProviderService#performControlAction} indicating that the action has been 128 * performed. The action may still fail later and the state may not change. 129 */ 130 public static final @ResponseResult int RESPONSE_OK = 1; 131 /** 132 * Response code for the {@code consumer} in 133 * {@link ControlsProviderService#performControlAction} indicating that the action has failed. 134 */ 135 public static final @ResponseResult int RESPONSE_FAIL = 2; 136 /** 137 * Response code for the {@code consumer} in 138 * {@link ControlsProviderService#performControlAction} indicating that in order for the action 139 * to be performed, acknowledgment from the user is required. Any non-empty string returned 140 * from {@link #getChallengeValue} shall be treated as a positive acknowledgment. 141 */ 142 public static final @ResponseResult int RESPONSE_CHALLENGE_ACK = 3; 143 /** 144 * Response code for the {@code consumer} in 145 * {@link ControlsProviderService#performControlAction} indicating that in order for the action 146 * to be performed, a PIN is required. 147 */ 148 public static final @ResponseResult int RESPONSE_CHALLENGE_PIN = 4; 149 /** 150 * Response code for the {@code consumer} in 151 * {@link ControlsProviderService#performControlAction} indicating that in order for the action 152 * to be performed, an alphanumeric passphrase is required. 153 */ 154 public static final @ResponseResult int RESPONSE_CHALLENGE_PASSPHRASE = 5; 155 156 /** 157 * The {@link ActionType} associated with this class. 158 */ getActionType()159 public abstract @ActionType int getActionType(); 160 161 private final @NonNull String mTemplateId; 162 private final @Nullable String mChallengeValue; 163 ControlAction()164 private ControlAction() { 165 mTemplateId = ""; 166 mChallengeValue = null; 167 } 168 169 /** 170 * @hide 171 */ ControlAction(@onNull String templateId, @Nullable String challengeValue)172 ControlAction(@NonNull String templateId, @Nullable String challengeValue) { 173 Preconditions.checkNotNull(templateId); 174 mTemplateId = templateId; 175 mChallengeValue = challengeValue; 176 } 177 178 /** 179 * @hide 180 */ ControlAction(Bundle b)181 ControlAction(Bundle b) { 182 mTemplateId = b.getString(KEY_TEMPLATE_ID); 183 mChallengeValue = b.getString(KEY_CHALLENGE_VALUE); 184 } 185 186 /** 187 * The identifier of the {@link ControlTemplate} that originated this action 188 */ 189 @NonNull getTemplateId()190 public String getTemplateId() { 191 return mTemplateId; 192 } 193 194 /** 195 * The challenge value used to authenticate certain actions, if available. 196 */ 197 @Nullable getChallengeValue()198 public String getChallengeValue() { 199 return mChallengeValue; 200 } 201 202 /** 203 * Obtain a {@link Bundle} describing this object populated with data. 204 * 205 * Implementations in subclasses should populate the {@link Bundle} returned by 206 * {@link ControlAction}. 207 * @return a {@link Bundle} containing the data that represents this object. 208 * @hide 209 */ 210 @CallSuper 211 @NonNull getDataBundle()212 Bundle getDataBundle() { 213 Bundle b = new Bundle(); 214 b.putInt(KEY_ACTION_TYPE, getActionType()); 215 b.putString(KEY_TEMPLATE_ID, mTemplateId); 216 b.putString(KEY_CHALLENGE_VALUE, mChallengeValue); 217 return b; 218 } 219 220 /** 221 * @param bundle 222 * @return 223 * @hide 224 */ 225 @NonNull createActionFromBundle(@onNull Bundle bundle)226 static ControlAction createActionFromBundle(@NonNull Bundle bundle) { 227 if (bundle == null) { 228 Log.e(TAG, "Null bundle"); 229 return ERROR_ACTION; 230 } 231 int type = bundle.getInt(KEY_ACTION_TYPE, TYPE_ERROR); 232 try { 233 switch (type) { 234 case TYPE_BOOLEAN: 235 return new BooleanAction(bundle); 236 case TYPE_FLOAT: 237 return new FloatAction(bundle); 238 case TYPE_MODE: 239 return new ModeAction(bundle); 240 case TYPE_COMMAND: 241 return new CommandAction(bundle); 242 case TYPE_ERROR: 243 default: 244 return ERROR_ACTION; 245 } 246 } catch (Exception e) { 247 Log.e(TAG, "Error creating action", e); 248 return ERROR_ACTION; 249 } 250 } 251 252 /** 253 * Returns a singleton {@link ControlAction} used for indicating an error in unparceling. 254 */ 255 @NonNull getErrorAction()256 public static ControlAction getErrorAction() { 257 return ERROR_ACTION; 258 } 259 } 260