• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5  * except in compliance with the License. You may obtain a copy of the License at
6  *
7  *      http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the
10  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11  * KIND, either express or implied. See the License for the specific language governing
12  * permissions and limitations under the License.
13  */
14 
15 package com.android.systemui;
16 
17 import static com.android.systemui.Flags.sliceBroadcastRelayInBackground;
18 
19 import android.content.BroadcastReceiver;
20 import android.content.ComponentName;
21 import android.content.ContentProvider;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.IntentFilter;
25 import android.net.Uri;
26 import android.os.UserHandle;
27 import android.util.ArrayMap;
28 import android.util.Log;
29 
30 import androidx.annotation.GuardedBy;
31 import androidx.annotation.WorkerThread;
32 
33 import com.android.internal.annotations.VisibleForTesting;
34 import com.android.settingslib.SliceBroadcastRelay;
35 import com.android.systemui.broadcast.BroadcastDispatcher;
36 import com.android.systemui.dagger.SysUISingleton;
37 import com.android.systemui.dagger.qualifiers.Background;
38 
39 import java.util.concurrent.CopyOnWriteArraySet;
40 import java.util.concurrent.Executor;
41 
42 import javax.inject.Inject;
43 
44 /**
45  * Allows settings to register certain broadcasts to launch the settings app for pinned slices.
46  * @see SliceBroadcastRelay
47  */
48 @SysUISingleton
49 public class SliceBroadcastRelayHandler implements CoreStartable {
50     private static final String TAG = "SliceBroadcastRelay";
51     private static final boolean DEBUG = false;
52 
53     @GuardedBy("mRelays")
54     private final ArrayMap<Uri, BroadcastRelay> mRelays = new ArrayMap<>();
55     private final Context mContext;
56     private final BroadcastDispatcher mBroadcastDispatcher;
57     private final Executor mBackgroundExecutor;
58 
59     @Inject
SliceBroadcastRelayHandler(Context context, BroadcastDispatcher broadcastDispatcher, @Background Executor backgroundExecutor)60     public SliceBroadcastRelayHandler(Context context, BroadcastDispatcher broadcastDispatcher,
61                                       @Background Executor backgroundExecutor) {
62         mContext = context;
63         mBroadcastDispatcher = broadcastDispatcher;
64         mBackgroundExecutor = backgroundExecutor;
65     }
66 
67     @Override
start()68     public void start() {
69         if (DEBUG) Log.d(TAG, "Start");
70         IntentFilter filter = new IntentFilter(SliceBroadcastRelay.ACTION_REGISTER);
71         filter.addAction(SliceBroadcastRelay.ACTION_UNREGISTER);
72 
73         if (sliceBroadcastRelayInBackground()) {
74             mBroadcastDispatcher.registerReceiver(mReceiver, filter, mBackgroundExecutor);
75         } else {
76             mBroadcastDispatcher.registerReceiver(mReceiver, filter);
77         }
78     }
79 
80     // This does not use BroadcastDispatcher as the filter may have schemas or mime types.
81     @WorkerThread
82     @VisibleForTesting
handleIntent(Intent intent)83     void handleIntent(Intent intent) {
84         if (SliceBroadcastRelay.ACTION_REGISTER.equals(intent.getAction())) {
85             Uri uri = intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_URI, Uri.class);
86             ComponentName receiverClass =
87                     intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_RECEIVER,
88                             ComponentName.class);
89             IntentFilter filter = intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_FILTER,
90                     IntentFilter.class);
91             if (DEBUG) Log.d(TAG, "Register " + uri + " " + receiverClass + " " + filter);
92             getOrCreateRelay(uri).register(mContext, receiverClass, filter);
93         } else if (SliceBroadcastRelay.ACTION_UNREGISTER.equals(intent.getAction())) {
94             Uri uri = intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_URI, Uri.class);
95             if (DEBUG) Log.d(TAG, "Unregister " + uri);
96             BroadcastRelay relay = getAndRemoveRelay(uri);
97             if (relay != null) {
98                 relay.unregister(mContext);
99             }
100         }
101     }
102 
103     @WorkerThread
getOrCreateRelay(Uri uri)104     private BroadcastRelay getOrCreateRelay(Uri uri) {
105         synchronized (mRelays) {
106             BroadcastRelay ret = mRelays.get(uri);
107             if (ret == null) {
108                 ret = new BroadcastRelay(uri);
109                 mRelays.put(uri, ret);
110             }
111             return ret;
112         }
113     }
114 
115     @WorkerThread
getAndRemoveRelay(Uri uri)116     private BroadcastRelay getAndRemoveRelay(Uri uri) {
117         synchronized (mRelays) {
118             return mRelays.remove(uri);
119         }
120     }
121 
122     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
123         @Override
124         public void onReceive(Context context, Intent intent) {
125             handleIntent(intent);
126         }
127     };
128 
129     private static class BroadcastRelay extends BroadcastReceiver {
130 
131         private final CopyOnWriteArraySet<ComponentName> mReceivers = new CopyOnWriteArraySet<>();
132         private final UserHandle mUserId;
133         private final Uri mUri;
134 
BroadcastRelay(Uri uri)135         public BroadcastRelay(Uri uri) {
136             mUserId = new UserHandle(ContentProvider.getUserIdFromUri(uri));
137             mUri = uri;
138         }
139 
register(Context context, ComponentName receiver, IntentFilter filter)140         public void register(Context context, ComponentName receiver, IntentFilter filter) {
141             mReceivers.add(receiver);
142             context.registerReceiver(this, filter,
143                     Context.RECEIVER_EXPORTED_UNAUDITED);
144         }
145 
unregister(Context context)146         public void unregister(Context context) {
147             context.unregisterReceiver(this);
148         }
149 
150         @Override
onReceive(Context context, Intent intent)151         public void onReceive(Context context, Intent intent) {
152             intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
153             for (ComponentName receiver : mReceivers) {
154                 intent.setComponent(receiver);
155                 intent.putExtra(SliceBroadcastRelay.EXTRA_URI, mUri.toString());
156                 if (DEBUG) Log.d(TAG, "Forwarding " + receiver + " " + intent + " " + mUserId);
157                 context.sendBroadcastAsUser(intent, mUserId);
158             }
159         }
160     }
161 }
162