blob: f34df75a9ab9b1d6b9d944574abb806eb83ff516 [file] [log] [blame]
Jason Monkd5a204f2015-12-21 08:50:01 -05001/*
2 * Copyright (C) 2015 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 */
16package com.android.systemui.qs.external;
17
18import android.app.ActivityManager;
Jason Monk07f55a22016-04-11 15:30:38 -040019import android.content.BroadcastReceiver;
Jason Monkd5a204f2015-12-21 08:50:01 -050020import android.content.ComponentName;
Jason Monk07f55a22016-04-11 15:30:38 -040021import android.content.Context;
Jason Monkd5a204f2015-12-21 08:50:01 -050022import android.content.Intent;
Jason Monk07f55a22016-04-11 15:30:38 -040023import android.content.IntentFilter;
24import android.net.Uri;
Jason Monkd5a204f2015-12-21 08:50:01 -050025import android.os.Handler;
26import android.os.UserHandle;
27import android.service.quicksettings.IQSTileService;
28import android.support.annotation.VisibleForTesting;
29import android.util.Log;
Jason Monk07f55a22016-04-11 15:30:38 -040030import libcore.util.Objects;
Jason Monkd5a204f2015-12-21 08:50:01 -050031
32/**
33 * Manages the priority which lets {@link TileServices} make decisions about which tiles
34 * to bind. Also holds on to and manages the {@link TileLifecycleManager}, informing it
35 * of when it is allowed to bind based on decisions frome the {@link TileServices}.
36 */
37public class TileServiceManager {
38
39 private static final long MIN_BIND_TIME = 5000;
40 private static final long UNBIND_DELAY = 30000;
41
42 public static final boolean DEBUG = true;
43
44 private static final String TAG = "TileServiceManager";
45
Jason Monkfe8f6822015-12-21 15:12:01 -050046 @VisibleForTesting
47 static final String PREFS_FILE = "CustomTileModes";
48
Jason Monkd5a204f2015-12-21 08:50:01 -050049 private final TileServices mServices;
50 private final TileLifecycleManager mStateManager;
51 private final Handler mHandler;
52 private boolean mBindRequested;
53 private boolean mBindAllowed;
54 private boolean mBound;
55 private int mPriority;
56 private boolean mJustBound;
57 private long mLastUpdate;
Jason Monk34a5cef2016-01-29 11:28:44 -050058 private boolean mShowingDialog;
Jason Monk1c2fea82016-03-11 11:33:36 -050059 // Whether we have a pending bind going out to the service without a response yet.
60 // This defaults to true to ensure tiles start out unavailable.
61 private boolean mPendingBind = true;
Jason Monkd5a204f2015-12-21 08:50:01 -050062
63 TileServiceManager(TileServices tileServices, Handler handler, ComponentName component) {
64 this(tileServices, handler, new TileLifecycleManager(handler,
65 tileServices.getContext(), new Intent().setComponent(component),
66 new UserHandle(ActivityManager.getCurrentUser())));
67 }
68
69 @VisibleForTesting
70 TileServiceManager(TileServices tileServices, Handler handler,
71 TileLifecycleManager tileLifecycleManager) {
72 mServices = tileServices;
73 mHandler = handler;
74 mStateManager = tileLifecycleManager;
Jason Monkfe8f6822015-12-21 15:12:01 -050075 mStateManager.setQSService(tileServices);
Jason Monk07f55a22016-04-11 15:30:38 -040076
77 IntentFilter filter = new IntentFilter();
78 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
79 filter.addDataScheme("package");
80 mServices.getContext().registerReceiverAsUser(mUninstallReceiver,
81 new UserHandle(ActivityManager.getCurrentUser()), filter, null, mHandler);
Jason Monkfe8f6822015-12-21 15:12:01 -050082 }
83
Jason Monk97d22722016-04-07 11:41:47 -040084 public boolean isActiveTile() {
85 return mStateManager.isActiveTile();
Jason Monkd5a204f2015-12-21 08:50:01 -050086 }
87
Jason Monk34a5cef2016-01-29 11:28:44 -050088 public void setShowingDialog(boolean dialog) {
89 mShowingDialog = dialog;
90 }
91
Jason Monkd5a204f2015-12-21 08:50:01 -050092 public IQSTileService getTileService() {
93 return mStateManager;
94 }
95
96 public void setBindRequested(boolean bindRequested) {
97 if (mBindRequested == bindRequested) return;
98 mBindRequested = bindRequested;
99 if (mBindAllowed && mBindRequested && !mBound) {
Jason Monkfe8f6822015-12-21 15:12:01 -0500100 mHandler.removeCallbacks(mUnbind);
Jason Monkd5a204f2015-12-21 08:50:01 -0500101 bindService();
102 } else {
103 mServices.recalculateBindAllowance();
104 }
105 if (mBound && !mBindRequested) {
Jason Monkfe8f6822015-12-21 15:12:01 -0500106 mHandler.postDelayed(mUnbind, UNBIND_DELAY);
Jason Monkd5a204f2015-12-21 08:50:01 -0500107 }
108 }
109
110 public void setLastUpdate(long lastUpdate) {
111 mLastUpdate = lastUpdate;
Jason Monk97d22722016-04-07 11:41:47 -0400112 if (mBound && isActiveTile()) {
Jason Monkfe8f6822015-12-21 15:12:01 -0500113 mStateManager.onStopListening();
114 setBindRequested(false);
115 }
Jason Monkd5a204f2015-12-21 08:50:01 -0500116 mServices.recalculateBindAllowance();
117 }
118
119 public void handleDestroy() {
Jason Monk07f55a22016-04-11 15:30:38 -0400120 mServices.getContext().unregisterReceiver(mUninstallReceiver);
Jason Monkd5a204f2015-12-21 08:50:01 -0500121 mStateManager.handleDestroy();
122 }
123
124 public void setBindAllowed(boolean allowed) {
125 if (mBindAllowed == allowed) return;
126 mBindAllowed = allowed;
127 if (!mBindAllowed && mBound) {
128 unbindService();
129 } else if (mBindAllowed && mBindRequested && !mBound) {
130 bindService();
131 }
132 }
133
Jason Monk1c2fea82016-03-11 11:33:36 -0500134 public boolean hasPendingBind() {
135 return mPendingBind;
136 }
137
138 public void clearPendingBind() {
139 mPendingBind = false;
140 }
141
Jason Monkd5a204f2015-12-21 08:50:01 -0500142 private void bindService() {
143 if (mBound) {
144 Log.e(TAG, "Service already bound");
145 return;
146 }
Jason Monk1c2fea82016-03-11 11:33:36 -0500147 mPendingBind = true;
Jason Monkd5a204f2015-12-21 08:50:01 -0500148 mBound = true;
149 mJustBound = true;
150 mHandler.postDelayed(mJustBoundOver, MIN_BIND_TIME);
151 mStateManager.setBindService(true);
152 }
153
154 private void unbindService() {
155 if (!mBound) {
156 Log.e(TAG, "Service not bound");
157 return;
158 }
159 mBound = false;
160 mJustBound = false;
161 mStateManager.setBindService(false);
162 }
163
164 public void calculateBindPriority(long currentTime) {
165 if (mStateManager.hasPendingClick()) {
166 // Pending click is the most important thing, need to put this service at the top of
167 // the list to be bound.
168 mPriority = Integer.MAX_VALUE;
Jason Monk34a5cef2016-01-29 11:28:44 -0500169 } else if (mShowingDialog) {
170 // Hang on to services that are showing dialogs so they don't die.
171 mPriority = Integer.MAX_VALUE - 1;
Jason Monkd5a204f2015-12-21 08:50:01 -0500172 } else if (mJustBound) {
173 // If we just bound, lets not thrash on binding/unbinding too much, this is second most
174 // important.
Jason Monk34a5cef2016-01-29 11:28:44 -0500175 mPriority = Integer.MAX_VALUE - 2;
Jason Monkd5a204f2015-12-21 08:50:01 -0500176 } else if (!mBindRequested) {
177 // Don't care about binding right now, put us last.
178 mPriority = Integer.MIN_VALUE;
179 } else {
180 // Order based on whether this was just updated.
181 long timeSinceUpdate = currentTime - mLastUpdate;
182 // Fit compare into integer space for simplicity. Make sure to leave MAX_VALUE and
183 // MAX_VALUE - 1 for the more important states above.
Jason Monk34a5cef2016-01-29 11:28:44 -0500184 if (timeSinceUpdate > Integer.MAX_VALUE - 3) {
185 mPriority = Integer.MAX_VALUE - 3;
Jason Monkd5a204f2015-12-21 08:50:01 -0500186 } else {
187 mPriority = (int) timeSinceUpdate;
188 }
189 }
190 }
191
192 public int getBindPriority() {
193 return mPriority;
194 }
195
Jason Monkfe8f6822015-12-21 15:12:01 -0500196 private final Runnable mUnbind = new Runnable() {
197 @Override
198 public void run() {
199 if (mBound && !mBindRequested) {
200 unbindService();
201 }
202 }
203 };
204
Jason Monkd5a204f2015-12-21 08:50:01 -0500205 @VisibleForTesting
206 final Runnable mJustBoundOver = new Runnable() {
207 @Override
208 public void run() {
209 mJustBound = false;
210 mServices.recalculateBindAllowance();
211 }
212 };
Jason Monk07f55a22016-04-11 15:30:38 -0400213
214 private final BroadcastReceiver mUninstallReceiver = new BroadcastReceiver() {
215 @Override
216 public void onReceive(Context context, Intent intent) {
217 if (!Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
218 return;
219 }
220 if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
221 return;
222 }
223 Uri data = intent.getData();
224 String pkgName = data.getEncodedSchemeSpecificPart();
225 final ComponentName component = mStateManager.getComponent();
226 if (!Objects.equal(pkgName, component.getPackageName())) {
227 return;
228 }
229 mServices.getHost().removeTile(component);
230 }
231 };
Jason Monkd5a204f2015-12-21 08:50:01 -0500232}