• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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