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.bedstead.dpmwrapper; 17 18 import static com.android.bedstead.dpmwrapper.Utils.MY_USER_ID; 19 import static com.android.bedstead.dpmwrapper.Utils.VERBOSE; 20 import static com.android.bedstead.dpmwrapper.Utils.assertCurrentUserOnHeadlessSystemMode; 21 22 import android.app.ActivityManager; 23 import android.content.BroadcastReceiver; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.IntentFilter; 27 import android.os.Handler; 28 import android.os.HandlerThread; 29 import android.os.UserHandle; 30 import android.util.ArrayMap; 31 import android.util.Log; 32 33 import com.android.internal.annotations.GuardedBy; 34 35 import java.util.ArrayList; 36 37 /** 38 * {@link BroadcastReceiver} used in the test apps to receive intents that were originally sent to 39 * the device owner's {@link android.app.admin.DeviceAdminReceiver}. 40 * 41 * <p>It must be declared in the manifest: 42 * <pre><code> 43 <receiver android:name="com.android.bedstead.dpmwrapper.TestAppCallbacksReceiver" 44 android:exported="true"/> 45 </code></pre> 46 * 47 */ 48 public final class TestAppCallbacksReceiver extends BroadcastReceiver { 49 50 private static final String TAG = TestAppCallbacksReceiver.class.getSimpleName(); 51 private static final String EXTRA = "relayed_intent"; 52 53 private static final Object LOCK = new Object(); 54 private static HandlerThread sHandlerThread; 55 private static Handler sHandler; 56 57 /** 58 * Map of receivers per intent action. 59 */ 60 @GuardedBy("LOCK") 61 private static final ArrayMap<String, ArrayList<BroadcastReceiver>> sRealReceivers = 62 new ArrayMap<>(); 63 setHandlerThread()64 private static void setHandlerThread() { 65 if (sHandlerThread != null) return; 66 67 sHandlerThread = new HandlerThread("TestAppCallbacksReceiverThread"); 68 Log.i(TAG, "Starting thread " + sHandlerThread + " on user " + MY_USER_ID); 69 sHandlerThread.start(); 70 sHandler = new Handler(sHandlerThread.getLooper()); 71 } 72 73 @Override onReceive(Context context, Intent intent)74 public void onReceive(Context context, Intent intent) { 75 Log.i(TAG, " received intent on user " + context.getUserId() + ": " + intent); 76 assertCurrentUserOnHeadlessSystemMode(context); 77 setHandlerThread(); 78 79 Intent realIntent = intent.getParcelableExtra(EXTRA); 80 if (realIntent == null) { 81 Log.e(TAG, "No " + EXTRA + " on intent " + intent); 82 return; 83 } 84 String action = realIntent.getAction(); 85 ArrayList<BroadcastReceiver> receivers; 86 synchronized (LOCK) { 87 receivers = sRealReceivers.get(action); 88 } 89 if (receivers == null || receivers.isEmpty()) { 90 Log.e(TAG, "onReceive(): no receiver for " + action + ": " + sRealReceivers); 91 return; 92 } 93 Log.d(TAG, "Will dispatch intent to " + receivers.size() + " on handler thread"); 94 receivers.forEach((r) -> sHandler.post(() -> 95 handleDispatchIntent(r, context, realIntent))); 96 } 97 handleDispatchIntent(BroadcastReceiver receiver, Context context, Intent intent)98 private void handleDispatchIntent(BroadcastReceiver receiver, Context context, 99 Intent intent) { 100 Log.d(TAG, "Dispatching " + intent + " to " + receiver + " on thread " 101 + Thread.currentThread()); 102 receiver.onReceive(context, intent); 103 } 104 registerReceiver(Context context, BroadcastReceiver receiver, IntentFilter filter)105 static void registerReceiver(Context context, BroadcastReceiver receiver, 106 IntentFilter filter) { 107 if (VERBOSE) Log.v(TAG, "registerReceiver(): " + receiver); 108 synchronized (LOCK) { 109 filter.actionsIterator().forEachRemaining((action) -> { 110 Log.d(TAG, "Registering " + receiver + " for " + action); 111 ArrayList<BroadcastReceiver> receivers = sRealReceivers.get(action); 112 if (receivers == null) { 113 receivers = new ArrayList<>(); 114 if (VERBOSE) Log.v(TAG, "Creating list of receivers for " + action); 115 sRealReceivers.put(action, receivers); 116 } 117 receivers.add(receiver); 118 }); 119 } 120 } 121 unregisterReceiver(Context context, BroadcastReceiver receiver)122 static void unregisterReceiver(Context context, BroadcastReceiver receiver) { 123 if (VERBOSE) Log.v(TAG, "unregisterReceiver(): " + receiver); 124 125 synchronized (LOCK) { 126 for (int i = 0; i < sRealReceivers.size(); i++) { 127 String action = sRealReceivers.keyAt(i); 128 ArrayList<BroadcastReceiver> receivers = sRealReceivers.valueAt(i); 129 boolean removed = receivers.remove(receiver); 130 if (removed) { 131 Log.d(TAG, "Removed " + receiver + " for action " + action); 132 } 133 } 134 } 135 } 136 sendBroadcast(Context context, Intent intent)137 static void sendBroadcast(Context context, Intent intent) { 138 int currentUserId = ActivityManager.getCurrentUser(); 139 Intent bridgeIntent = new Intent(context, TestAppCallbacksReceiver.class) 140 .putExtra(EXTRA, intent); 141 Log.d(TAG, "Relaying " + intent + " from user " + MY_USER_ID + " to user " 142 + currentUserId + " using " + bridgeIntent); 143 context.sendBroadcastAsUser(bridgeIntent, UserHandle.of(currentUserId)); 144 } 145 } 146