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