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 android.content.BroadcastReceiver;
18 import android.content.ComponentName;
19 import android.content.ContentProvider;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.IntentFilter;
23 import android.net.Uri;
24 import android.os.UserHandle;
25 import android.util.ArrayMap;
26 import android.util.ArraySet;
27 import android.util.Log;
28 
29 import com.android.internal.annotations.VisibleForTesting;
30 import com.android.settingslib.SliceBroadcastRelay;
31 import com.android.systemui.broadcast.BroadcastDispatcher;
32 import com.android.systemui.dagger.SysUISingleton;
33 
34 import javax.inject.Inject;
35 
36 /**
37  * Allows settings to register certain broadcasts to launch the settings app for pinned slices.
38  * @see SliceBroadcastRelay
39  */
40 @SysUISingleton
41 public class SliceBroadcastRelayHandler implements CoreStartable {
42     private static final String TAG = "SliceBroadcastRelay";
43     private static final boolean DEBUG = false;
44 
45     private final ArrayMap<Uri, BroadcastRelay> mRelays = new ArrayMap<>();
46     private final Context mContext;
47     private final BroadcastDispatcher mBroadcastDispatcher;
48 
49     @Inject
SliceBroadcastRelayHandler(Context context, BroadcastDispatcher broadcastDispatcher)50     public SliceBroadcastRelayHandler(Context context, BroadcastDispatcher broadcastDispatcher) {
51         mContext = context;
52         mBroadcastDispatcher = broadcastDispatcher;
53     }
54 
55     @Override
start()56     public void start() {
57         if (DEBUG) Log.d(TAG, "Start");
58         IntentFilter filter = new IntentFilter(SliceBroadcastRelay.ACTION_REGISTER);
59         filter.addAction(SliceBroadcastRelay.ACTION_UNREGISTER);
60         mBroadcastDispatcher.registerReceiver(mReceiver, filter);
61     }
62 
63     // This does not use BroadcastDispatcher as the filter may have schemas or mime types.
64     @VisibleForTesting
handleIntent(Intent intent)65     void handleIntent(Intent intent) {
66         if (SliceBroadcastRelay.ACTION_REGISTER.equals(intent.getAction())) {
67             Uri uri = intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_URI);
68             ComponentName receiverClass =
69                     intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_RECEIVER);
70             IntentFilter filter = intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_FILTER);
71             if (DEBUG) Log.d(TAG, "Register " + uri + " " + receiverClass + " " + filter);
72             getOrCreateRelay(uri).register(mContext, receiverClass, filter);
73         } else if (SliceBroadcastRelay.ACTION_UNREGISTER.equals(intent.getAction())) {
74             Uri uri = intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_URI);
75             if (DEBUG) Log.d(TAG, "Unregister " + uri);
76             BroadcastRelay relay = getAndRemoveRelay(uri);
77             if (relay != null) {
78                 relay.unregister(mContext);
79             }
80         }
81     }
82 
getOrCreateRelay(Uri uri)83     private BroadcastRelay getOrCreateRelay(Uri uri) {
84         BroadcastRelay ret = mRelays.get(uri);
85         if (ret == null) {
86             ret = new BroadcastRelay(uri);
87             mRelays.put(uri, ret);
88         }
89         return ret;
90     }
91 
getAndRemoveRelay(Uri uri)92     private BroadcastRelay getAndRemoveRelay(Uri uri) {
93         return mRelays.remove(uri);
94     }
95 
96     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
97         @Override
98         public void onReceive(Context context, Intent intent) {
99             handleIntent(intent);
100         }
101     };
102 
103     private static class BroadcastRelay extends BroadcastReceiver {
104 
105         private final ArraySet<ComponentName> mReceivers = new ArraySet<>();
106         private final UserHandle mUserId;
107         private final Uri mUri;
108 
BroadcastRelay(Uri uri)109         public BroadcastRelay(Uri uri) {
110             mUserId = new UserHandle(ContentProvider.getUserIdFromUri(uri));
111             mUri = uri;
112         }
113 
register(Context context, ComponentName receiver, IntentFilter filter)114         public void register(Context context, ComponentName receiver, IntentFilter filter) {
115             mReceivers.add(receiver);
116             context.registerReceiver(this, filter,
117                     Context.RECEIVER_EXPORTED_UNAUDITED);
118         }
119 
unregister(Context context)120         public void unregister(Context context) {
121             context.unregisterReceiver(this);
122         }
123 
124         @Override
onReceive(Context context, Intent intent)125         public void onReceive(Context context, Intent intent) {
126             intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
127             for (ComponentName receiver : mReceivers) {
128                 intent.setComponent(receiver);
129                 intent.putExtra(SliceBroadcastRelay.EXTRA_URI, mUri.toString());
130                 if (DEBUG) Log.d(TAG, "Forwarding " + receiver + " " + intent + " " + mUserId);
131                 context.sendBroadcastAsUser(intent, mUserId);
132             }
133         }
134     }
135 }
136