1 /*
2  * Copyright (C) 2021 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 package com.android.cts.devicepolicy;
17 
18 import static android.app.admin.DevicePolicyManager.operationToString;
19 
20 import android.content.BroadcastReceiver;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.IntentFilter;
24 import android.util.Log;
25 
26 import com.android.bedstead.dpmwrapper.TestAppHelper;
27 
28 import junit.framework.AssertionFailedError;
29 
30 import java.util.concurrent.LinkedBlockingQueue;
31 import java.util.concurrent.TimeUnit;
32 
33 //TODO(b/174859111): move to automotive-only section
34 /**
35  * Helper class used by test apps to get the safety event received by the device owner's
36  * {@link android.app.admin.DeviceAdminReceiver}.
37  */
38 public final class OperationSafetyChangedCallback {
39 
40     private static final String TAG = OperationSafetyChangedCallback.class.getSimpleName();
41 
42     private static final String ACTION_STATE_CHANGED = "operation_safety_state_changed";
43     private static final String EXTRA_REASON = "reason";
44     private static final String EXTRA_IS_SAFE = "is_safe";
45 
46     private static final long TIMEOUT_MS = 50_000;
47 
48     private final LinkedBlockingQueue<OperationSafetyChangedEvent> mEvents =
49             new LinkedBlockingQueue<>();
50 
51     private final boolean mForDeviceOwner;
52 
53     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
54         @Override
55         public void onReceive(Context context, Intent intent) {
56             String action = intent.getAction();
57             Log.v(TAG, "onReceive(): " + intent);
58             if (!ACTION_STATE_CHANGED.equals(action)) {
59                 Log.e(TAG, "Invalid action " + action + " on intent " + intent);
60                 return;
61             }
62             if (!intent.hasExtra(EXTRA_REASON)) {
63                 Log.e(TAG, "No " + EXTRA_REASON + " extra on intent " + intent);
64                 return;
65             }
66             if (!intent.hasExtra(EXTRA_IS_SAFE)) {
67                 Log.e(TAG, "No " + EXTRA_IS_SAFE + " extra on intent " + intent);
68                 return;
69             }
70             OperationSafetyChangedEvent event = new OperationSafetyChangedEvent(
71                     intent.getIntExtra(EXTRA_REASON, 42),
72                     intent.getBooleanExtra(EXTRA_IS_SAFE, false));
73             Log.d(TAG, "Received intent with event " + event + " on user " + context.getUserId());
74             mEvents.offer(event);
75         }
76     };
77 
OperationSafetyChangedCallback(boolean forDeviceOwner)78     private OperationSafetyChangedCallback(boolean forDeviceOwner) {
79         mForDeviceOwner = forDeviceOwner;
80     }
81 
82     /**
83      * Creates and registers a callback in the given context.
84      */
register(Context context, boolean forDeviceOwner)85     public static OperationSafetyChangedCallback register(Context context, boolean forDeviceOwner) {
86         Log.d(TAG, "Registering " + ACTION_STATE_CHANGED + " on user " + context.getUserId());
87         OperationSafetyChangedCallback callback = new OperationSafetyChangedCallback(
88                 forDeviceOwner);
89         TestAppHelper.registerTestCaseReceiver(context, callback.mReceiver,
90                 new IntentFilter(ACTION_STATE_CHANGED), forDeviceOwner);
91         return callback;
92     }
93 
94     /**
95      * Unregister this callback in the given context.
96      */
unregister(Context context)97     public void unregister(Context context) {
98         Log.d(TAG, "Unregistering " + mReceiver + " on user " + context.getUserId());
99         TestAppHelper.unregisterTestCaseReceiver(context, mReceiver, mForDeviceOwner);
100     }
101 
102     /**
103      * Gets the intent for the given event.
104      */
intentFor(OperationSafetyChangedEvent event)105     public static Intent intentFor(OperationSafetyChangedEvent event) {
106         return new Intent(ACTION_STATE_CHANGED)
107                 .putExtra(EXTRA_REASON, event.reason)
108                 .putExtra(EXTRA_IS_SAFE, event.isSafe);
109     }
110 
111     /**
112      * Gets next event or fail.
113      */
getNextEvent(int operation)114     public OperationSafetyChangedEvent getNextEvent(int operation) {
115         OperationSafetyChangedEvent event = null;
116         try {
117             event = mEvents.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS);
118         } catch (InterruptedException e) {
119             String msg = "interrupted waiting for event";
120             Log.e(TAG, msg, e);
121             Thread.currentThread().interrupt();
122             throw new AssertionFailedError(msg);
123         }
124         if (event == null) {
125             String msg = "didn't receive an OperationSafetyChangedEvent for "
126                     + operationToString(operation) + " in " + TIMEOUT_MS + "ms on " + this;
127             Log.e(TAG, msg);
128             throw new AssertionFailedError(msg);
129         }
130         return event;
131     }
132 
133     @Override
toString()134     public String toString() {
135         return "OperationSafetyChangedCallback[events=" + mEvents + "]";
136     }
137 }
138