1 /* 2 * Copyright (C) 2024 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.security.intrusiondetection; 18 19 import static android.Manifest.permission.MANAGE_INTRUSION_DETECTION_STATE; 20 import static android.Manifest.permission.READ_INTRUSION_DETECTION_STATE; 21 22 import android.annotation.CallbackExecutor; 23 import android.annotation.FlaggedApi; 24 import android.annotation.IntDef; 25 import android.annotation.NonNull; 26 import android.annotation.RequiresPermission; 27 import android.annotation.SystemApi; 28 import android.annotation.SystemService; 29 import android.content.Context; 30 import android.os.RemoteException; 31 import android.security.Flags; 32 import android.util.Log; 33 34 import java.lang.annotation.ElementType; 35 import java.lang.annotation.Retention; 36 import java.lang.annotation.RetentionPolicy; 37 import java.lang.annotation.Target; 38 import java.util.Objects; 39 import java.util.concurrent.ConcurrentHashMap; 40 import java.util.concurrent.Executor; 41 import java.util.function.Consumer; 42 43 /** 44 * IntrusionDetectionManager manages the intrusion detection on Android devices. 45 * Upon user consent, intrusion detection collects various device events for 46 * off-device investigation of potential device compromise. 47 * <p> 48 * Intrusion detection logging can either be enabled ({@link #STATE_ENABLED} 49 * or disabled ({@link #STATE_DISABLED}). 50 * <p> 51 * The intrusion detection logs will be transferred to 52 * {@link android.security.intrusiondetection.IntrusionDetectionEventTransport}. 53 * 54 * @hide 55 */ 56 @SystemApi 57 @FlaggedApi(Flags.FLAG_AFL_API) 58 @SystemService(Context.INTRUSION_DETECTION_SERVICE) 59 public class IntrusionDetectionManager { 60 private static final String TAG = "IntrusionDetectionManager"; 61 62 /** @hide */ 63 @Target(ElementType.TYPE_USE) 64 @Retention(RetentionPolicy.SOURCE) 65 @IntDef(prefix = { "STATE_" }, value = { 66 STATE_UNKNOWN, 67 STATE_DISABLED, 68 STATE_ENABLED 69 }) 70 public @interface IntrusionDetectionState {} 71 72 /** @hide */ 73 @Retention(RetentionPolicy.SOURCE) 74 @IntDef(prefix = { "ERROR_" }, value = { 75 ERROR_UNKNOWN, 76 ERROR_PERMISSION_DENIED, 77 ERROR_TRANSPORT_UNAVAILABLE, 78 ERROR_DATA_SOURCE_UNAVAILABLE 79 }) 80 public @interface IntrusionDetectionError {} 81 82 /** 83 * Indicates an unknown state 84 */ 85 public static final int STATE_UNKNOWN = IIntrusionDetectionServiceStateCallback.State.UNKNOWN; 86 87 /** 88 * Indicates an state that the intrusion detection is turned off. 89 */ 90 public static final int STATE_DISABLED = IIntrusionDetectionServiceStateCallback.State.DISABLED; 91 92 /** 93 * Indicates an state that the intrusion detection is turned on. 94 */ 95 public static final int STATE_ENABLED = IIntrusionDetectionServiceStateCallback.State.ENABLED; 96 97 /** 98 * Indicates an unknown error 99 */ 100 public static final int ERROR_UNKNOWN = 101 IIntrusionDetectionServiceCommandCallback.ErrorCode.UNKNOWN; 102 103 /** 104 * Indicates an error due to insufficient access rights. 105 */ 106 public static final int ERROR_PERMISSION_DENIED = 107 IIntrusionDetectionServiceCommandCallback.ErrorCode.PERMISSION_DENIED; 108 109 /** 110 * Indicates an error due to unavailability of the intrusion detection event transport. 111 */ 112 public static final int ERROR_TRANSPORT_UNAVAILABLE = 113 IIntrusionDetectionServiceCommandCallback.ErrorCode.TRANSPORT_UNAVAILABLE; 114 115 /** 116 * Indicates an error due to unavailability of the data source. 117 */ 118 public static final int ERROR_DATA_SOURCE_UNAVAILABLE = 119 IIntrusionDetectionServiceCommandCallback.ErrorCode.DATA_SOURCE_UNAVAILABLE; 120 121 122 private final IIntrusionDetectionService mService; 123 124 private final ConcurrentHashMap<Consumer<Integer>, IIntrusionDetectionServiceStateCallback> 125 mStateCallbacks = new ConcurrentHashMap<>(); 126 127 /** 128 * Constructor 129 * 130 * @param service A valid instance of IIntrusionDetectionService. 131 * @hide 132 */ IntrusionDetectionManager(IIntrusionDetectionService service)133 public IntrusionDetectionManager(IIntrusionDetectionService service) { 134 mService = service; 135 } 136 137 /** 138 * Add a callback to monitor the state of the IntrusionDetectionService. 139 * 140 * @param executor The executor through which the callback should be invoked. 141 * @param callback The callback for state change. 142 * Once the callback is registered, the callback will be called 143 * to reflect the init state. 144 * The callback can be registered only once. 145 */ 146 @RequiresPermission(READ_INTRUSION_DETECTION_STATE) addStateCallback(@onNull @allbackExecutor Executor executor, @NonNull @IntrusionDetectionState Consumer<Integer> callback)147 public void addStateCallback(@NonNull @CallbackExecutor Executor executor, 148 @NonNull @IntrusionDetectionState Consumer<Integer> callback) { 149 Objects.requireNonNull(executor); 150 Objects.requireNonNull(callback); 151 152 if (mStateCallbacks.get(callback) != null) { 153 Log.d(TAG, "addStateCallback callback already present"); 154 return; 155 } 156 157 final IIntrusionDetectionServiceStateCallback wrappedCallback = 158 new IIntrusionDetectionServiceStateCallback.Stub() { 159 @Override 160 public void onStateChange(int state) { 161 executor.execute(() -> callback.accept(state)); 162 } 163 }; 164 try { 165 mService.addStateCallback(wrappedCallback); 166 } catch (RemoteException e) { 167 throw e.rethrowFromSystemServer(); 168 } 169 170 mStateCallbacks.put(callback, wrappedCallback); 171 } 172 173 /** 174 * Remove a callback to monitor the state of the IntrusionDetectionService. 175 * 176 * @param callback The callback to remove. 177 */ 178 @RequiresPermission(READ_INTRUSION_DETECTION_STATE) removeStateCallback(@onNull Consumer<@IntrusionDetectionState Integer> callback)179 public void removeStateCallback(@NonNull Consumer<@IntrusionDetectionState Integer> callback) { 180 Objects.requireNonNull(callback); 181 if (!mStateCallbacks.containsKey(callback)) { 182 Log.d(TAG, "removeStateCallback callback not present"); 183 return; 184 } 185 186 IIntrusionDetectionServiceStateCallback wrappedCallback = mStateCallbacks.get(callback); 187 188 try { 189 mService.removeStateCallback(wrappedCallback); 190 } catch (RemoteException e) { 191 throw e.rethrowFromSystemServer(); 192 } 193 194 mStateCallbacks.remove(callback); 195 } 196 197 /** 198 * Enable intrusion detection. 199 * If successful, IntrusionDetectionService will transition to {@link #STATE_ENABLED} state. 200 * <p> 201 * When intrusion detection is enabled, various device events will be collected and 202 * sent over to the registered 203 * {@link android.security.intrusiondetection.IntrusionDetectionEventTransport}. 204 * 205 * @param executor The executor through which the callback should be invoked. 206 * @param callback The callback for the command result. 207 */ 208 @RequiresPermission(MANAGE_INTRUSION_DETECTION_STATE) enable(@onNull @allbackExecutor Executor executor, @NonNull CommandCallback callback)209 public void enable(@NonNull @CallbackExecutor Executor executor, 210 @NonNull CommandCallback callback) { 211 Objects.requireNonNull(executor); 212 Objects.requireNonNull(callback); 213 try { 214 mService.enable(new IIntrusionDetectionServiceCommandCallback.Stub() { 215 @Override 216 public void onSuccess() { 217 executor.execute(callback::onSuccess); 218 } 219 220 @Override 221 public void onFailure(int error) { 222 executor.execute(() -> callback.onFailure(error)); 223 } 224 }); 225 } catch (RemoteException e) { 226 throw e.rethrowFromSystemServer(); 227 } 228 } 229 230 /** 231 * Disable intrusion detection. 232 * If successful, IntrusionDetectionService will transition to {@link #STATE_DISABLED}. 233 * 234 * When intrusion detection is disabled, device events will no longer be collected. 235 * Any events that have been collected but not yet sent to IntrusionDetectionEventTransport 236 * will be transferred as a final batch. 237 * 238 * @param executor The executor through which the callback should be invoked. 239 * @param callback The callback for the command result. 240 */ 241 @RequiresPermission(MANAGE_INTRUSION_DETECTION_STATE) disable(@onNull @allbackExecutor Executor executor, @NonNull CommandCallback callback)242 public void disable(@NonNull @CallbackExecutor Executor executor, 243 @NonNull CommandCallback callback) { 244 Objects.requireNonNull(executor); 245 Objects.requireNonNull(callback); 246 try { 247 mService.disable(new IIntrusionDetectionServiceCommandCallback.Stub() { 248 @Override 249 public void onSuccess() { 250 executor.execute(callback::onSuccess); 251 } 252 253 @Override 254 public void onFailure(int error) { 255 executor.execute(() -> callback.onFailure(error)); 256 } 257 }); 258 } catch (RemoteException e) { 259 throw e.rethrowFromSystemServer(); 260 } 261 } 262 263 /** 264 * Callback used in {@link #enable} and {@link #disable} to indicate the result of the command. 265 */ 266 public interface CommandCallback { 267 /** 268 * Called when command succeeds. 269 */ onSuccess()270 void onSuccess(); 271 272 /** 273 * Called when command fails. 274 * @param error The error number. 275 */ onFailure(@ntrusionDetectionError int error)276 void onFailure(@IntrusionDetectionError int error); 277 } 278 } 279