blob: c4436f488887110e3f670de0dc67eec2dc314ec3 [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
Winsonc0d70582016-01-29 10:24:39 -080018import libcore.util.Objects;
19
Jason Monkd5a204f2015-12-21 08:50:01 -050020import android.app.AppGlobals;
21import android.content.BroadcastReceiver;
22import android.content.ComponentName;
23import android.content.Context;
24import android.content.Intent;
25import android.content.IntentFilter;
26import android.content.ServiceConnection;
27import android.content.pm.PackageManager;
28import android.content.pm.ServiceInfo;
29import android.net.Uri;
30import android.os.Handler;
31import android.os.IBinder;
32import android.os.RemoteException;
33import android.os.UserHandle;
Jason Monkfe8f6822015-12-21 15:12:01 -050034import android.service.quicksettings.IQSService;
Jason Monkd5a204f2015-12-21 08:50:01 -050035import android.service.quicksettings.IQSTileService;
36import android.service.quicksettings.Tile;
37import android.support.annotation.VisibleForTesting;
38import android.util.ArraySet;
39import android.util.Log;
Jason Monkd5a204f2015-12-21 08:50:01 -050040
41import java.util.Set;
42
43/**
44 * Manages the lifecycle of a TileService.
45 * <p>
46 * Will keep track of all calls on the IQSTileService interface and will relay those calls to the
47 * TileService as soon as it is bound. It will only bind to the service when it is allowed to
48 * ({@link #setBindService(boolean)}) and when the service is available.
49 */
50public class TileLifecycleManager extends BroadcastReceiver implements
51 IQSTileService, ServiceConnection, IBinder.DeathRecipient {
52 public static final boolean DEBUG = false;
53
54 private static final String TAG = "TileLifecycleManager";
55
56 private static final int MSG_ON_ADDED = 0;
57 private static final int MSG_ON_REMOVED = 1;
58 private static final int MSG_ON_CLICK = 2;
Jason Monk94295132016-01-12 11:27:02 -050059 private static final int MSG_ON_UNLOCK_COMPLETE = 3;
Jason Monkd5a204f2015-12-21 08:50:01 -050060
61 // Bind retry control.
62 private static final int MAX_BIND_RETRIES = 5;
63 private static final int BIND_RETRY_DELAY = 1000;
64
65 private final Context mContext;
66 private final Handler mHandler;
67 private final Intent mIntent;
68 private final UserHandle mUser;
69
70 private Set<Integer> mQueuedMessages = new ArraySet<>();
71 private QSTileServiceWrapper mWrapper;
72 private boolean mListening;
73 private Tile mTile;
74 private IBinder mClickBinder;
75
76 private int mBindTryCount;
77 private boolean mBound;
78 @VisibleForTesting
79 boolean mReceiverRegistered;
Jason Monkfe8f6822015-12-21 15:12:01 -050080 private IQSService mService;
Jason Monk51c444b2016-01-06 16:32:29 -050081 private boolean mUnbindImmediate;
Jason Monkd5a204f2015-12-21 08:50:01 -050082
83 public TileLifecycleManager(Handler handler, Context context, Intent intent, UserHandle user) {
84 mContext = context;
85 mHandler = handler;
86 mIntent = intent;
87 mUser = user;
88 }
89
Jason Monkfe8f6822015-12-21 15:12:01 -050090 public ComponentName getComponent() {
91 return mIntent.getComponent();
92 }
93
Jason Monkd5a204f2015-12-21 08:50:01 -050094 public boolean hasPendingClick() {
95 synchronized (mQueuedMessages) {
96 return mQueuedMessages.contains(MSG_ON_CLICK);
97 }
98 }
99
Jason Monk51c444b2016-01-06 16:32:29 -0500100 /**
101 * Binds just long enough to send any queued messages, then unbinds.
102 */
103 public void flushMessagesAndUnbind() {
104 mUnbindImmediate = true;
105 setBindService(true);
106 }
107
Jason Monkd5a204f2015-12-21 08:50:01 -0500108 public void setBindService(boolean bind) {
109 mBound = bind;
110 if (bind) {
111 if (mBindTryCount == MAX_BIND_RETRIES) {
112 // Too many failures, give up on this tile until an update.
113 startPackageListening();
114 return;
115 }
116 if (!checkComponentState()) {
117 return;
118 }
119 if (DEBUG) Log.d(TAG, "Binding service " + mIntent);
120 mBindTryCount++;
121 mContext.bindServiceAsUser(mIntent, this,
122 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
123 mUser);
124 } else {
125 if (DEBUG) Log.d(TAG, "Unbinding service " + mIntent);
126 // Give it another chance next time it needs to be bound, out of kindness.
127 mBindTryCount = 0;
Jason Monkfe8f6822015-12-21 15:12:01 -0500128 mWrapper = null;
Jason Monkd5a204f2015-12-21 08:50:01 -0500129 mContext.unbindService(this);
130 }
131 }
132
133 @Override
134 public void onServiceConnected(ComponentName name, IBinder service) {
135 if (DEBUG) Log.d(TAG, "onServiceConnected " + name);
136 // Got a connection, set the binding count to 0.
137 mBindTryCount = 0;
138 mWrapper = new QSTileServiceWrapper(Stub.asInterface(service));
139 try {
140 service.linkToDeath(this, 0);
141 } catch (RemoteException e) {
142 }
Jason Monkfe8f6822015-12-21 15:12:01 -0500143 setQSService(mService);
144 setQSTile(mTile);
Jason Monkd5a204f2015-12-21 08:50:01 -0500145 handlePendingMessages();
146 }
147
148 @Override
149 public void onServiceDisconnected(ComponentName name) {
150 if (DEBUG) Log.d(TAG, "onServiceDisconnected " + name);
151 mWrapper = null;
152 }
153
154 private void handlePendingMessages() {
155 // This ordering is laid out manually to make sure we preserve the TileService
156 // lifecycle.
157 ArraySet<Integer> queue;
158 synchronized (mQueuedMessages) {
159 queue = new ArraySet<>(mQueuedMessages);
160 mQueuedMessages.clear();
161 }
162 if (queue.contains(MSG_ON_ADDED)) {
163 if (DEBUG) Log.d(TAG, "Handling pending onAdded");
164 onTileAdded();
165 }
166 if (mListening) {
167 if (DEBUG) Log.d(TAG, "Handling pending onStartListening");
Jason Monkd5a204f2015-12-21 08:50:01 -0500168 onStartListening();
169 }
170 if (queue.contains(MSG_ON_CLICK)) {
171 if (DEBUG) Log.d(TAG, "Handling pending onClick");
172 if (!mListening) {
173 Log.w(TAG, "Managed to get click on non-listening state...");
174 // Skipping click since lost click privileges.
175 } else {
176 onClick(mClickBinder);
177 }
178 }
Jason Monk94295132016-01-12 11:27:02 -0500179 if (queue.contains(MSG_ON_UNLOCK_COMPLETE)) {
180 if (DEBUG) Log.d(TAG, "Handling pending onUnlockComplete");
181 if (!mListening) {
182 Log.w(TAG, "Managed to get unlock on non-listening state...");
183 // Skipping unlock since lost click privileges.
184 } else {
185 onUnlockComplete();
186 }
187 }
Jason Monkd5a204f2015-12-21 08:50:01 -0500188 if (queue.contains(MSG_ON_REMOVED)) {
189 if (DEBUG) Log.d(TAG, "Handling pending onRemoved");
190 if (mListening) {
191 Log.w(TAG, "Managed to get remove in listening state...");
192 onStopListening();
193 }
194 onTileRemoved();
195 }
Jason Monk51c444b2016-01-06 16:32:29 -0500196 if (mUnbindImmediate) {
197 mUnbindImmediate = false;
198 setBindService(false);
199 }
Jason Monkd5a204f2015-12-21 08:50:01 -0500200 }
201
202 public void handleDestroy() {
203 if (DEBUG) Log.d(TAG, "handleDestroy");
204 if (mReceiverRegistered) {
205 stopPackageListening();
206 }
207 }
208
209 private void handleDeath() {
210 if (mWrapper == null) return;
211 mWrapper = null;
212 if (!mBound) return;
213 if (DEBUG) Log.d(TAG, "handleDeath");
214 if (checkComponentState()) {
215 mHandler.postDelayed(new Runnable() {
216 @Override
217 public void run() {
218 if (mBound) {
219 // Retry binding.
220 setBindService(true);
221 }
222 }
223 }, BIND_RETRY_DELAY);
224 }
225 }
226
227 @Override
228 public void setQSTile(Tile tile) {
229 if (DEBUG) Log.d(TAG, "setQSTile " + tile);
230 mTile = tile;
231 if (mWrapper != null && !mWrapper.setQSTile(tile)) {
232 handleDeath();
233 }
234 }
235
236 private boolean checkComponentState() {
237 PackageManager pm = mContext.getPackageManager();
238 if (!isPackageAvailable(pm) || !isComponentAvailable(pm)) {
239 startPackageListening();
240 return false;
241 }
242 return true;
243 }
244
245 private void startPackageListening() {
246 if (DEBUG) Log.d(TAG, "startPackageListening");
247 IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
248 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
249 filter.addDataScheme("package");
250 mContext.registerReceiverAsUser(this, mUser, filter, null, mHandler);
251 mReceiverRegistered = true;
252 }
253
254 private void stopPackageListening() {
255 if (DEBUG) Log.d(TAG, "stopPackageListening");
256 mContext.unregisterReceiver(this);
257 mReceiverRegistered = false;
258 }
259
260 @Override
261 public void onReceive(Context context, Intent intent) {
262 if (DEBUG) Log.d(TAG, "onReceive: " + intent);
263 Uri data = intent.getData();
264 String pkgName = data.getEncodedSchemeSpecificPart();
265 if (!Objects.equal(pkgName, mIntent.getComponent().getPackageName())) {
266 return;
267 }
268 stopPackageListening();
269 if (mBound) {
270 // Trying to bind again will check the state of the package before bothering to bind.
271 if (DEBUG) Log.d(TAG, "Trying to rebind");
272 setBindService(true);
273 }
274 }
275
276 private boolean isComponentAvailable(PackageManager pm) {
277 String packageName = mIntent.getComponent().getPackageName();
278 try {
279 ServiceInfo si = AppGlobals.getPackageManager().getServiceInfo(mIntent.getComponent(),
280 0, mUser.getIdentifier());
281 if (DEBUG && si == null) Log.d(TAG, "Can't find component " + mIntent.getComponent());
282 return si != null;
283 } catch (RemoteException e) {
284 // Shouldn't happen.
285 }
286 return false;
287 }
288
289 private boolean isPackageAvailable(PackageManager pm) {
290 String packageName = mIntent.getComponent().getPackageName();
291 try {
292 pm.getPackageInfoAsUser(packageName, 0, mUser.getIdentifier());
293 return true;
294 } catch (PackageManager.NameNotFoundException e) {
295 if (DEBUG) Log.d(TAG, "Package not available: " + packageName, e);
296 else Log.d(TAG, "Package not available: " + packageName);
297 }
298 return false;
299 }
300
301 private void queueMessage(int message) {
302 synchronized (mQueuedMessages) {
303 mQueuedMessages.add(message);
304 }
305 }
306
307 @Override
Jason Monkfe8f6822015-12-21 15:12:01 -0500308 public void setQSService(IQSService service) {
309 mService = service;
310 if (mWrapper == null || !mWrapper.setQSService(service)) {
311 handleDeath();
312 }
313 }
314
315 @Override
Jason Monkd5a204f2015-12-21 08:50:01 -0500316 public void onTileAdded() {
317 if (DEBUG) Log.d(TAG, "onTileAdded");
318 if (mWrapper == null || !mWrapper.onTileAdded()) {
319 queueMessage(MSG_ON_ADDED);
320 handleDeath();
321 }
322 }
323
324 @Override
325 public void onTileRemoved() {
326 if (DEBUG) Log.d(TAG, "onTileRemoved");
327 if (mWrapper == null || !mWrapper.onTileRemoved()) {
328 queueMessage(MSG_ON_REMOVED);
329 handleDeath();
330 }
331 }
332
333 @Override
334 public void onStartListening() {
335 if (DEBUG) Log.d(TAG, "onStartListening");
336 mListening = true;
337 if (mWrapper != null && !mWrapper.onStartListening()) {
338 handleDeath();
339 }
340 }
341
342 @Override
343 public void onStopListening() {
344 if (DEBUG) Log.d(TAG, "onStopListening");
345 mListening = false;
346 if (mWrapper != null && !mWrapper.onStopListening()) {
347 handleDeath();
348 }
349 }
350
351 @Override
352 public void onClick(IBinder iBinder) {
353 if (DEBUG) Log.d(TAG, "onClick " + iBinder);
354 if (mWrapper == null || !mWrapper.onClick(iBinder)) {
355 mClickBinder = iBinder;
356 queueMessage(MSG_ON_CLICK);
357 handleDeath();
358 }
359 }
360
361 @Override
Jason Monk94295132016-01-12 11:27:02 -0500362 public void onUnlockComplete() {
363 if (DEBUG) Log.d(TAG, "onUnlockComplete");
364 if (mWrapper == null || !mWrapper.onUnlockComplete()) {
365 queueMessage(MSG_ON_UNLOCK_COMPLETE);
366 handleDeath();
367 }
368 }
369
370 @Override
Jason Monkd5a204f2015-12-21 08:50:01 -0500371 public IBinder asBinder() {
372 return mWrapper != null ? mWrapper.asBinder() : null;
373 }
374
375 @Override
376 public void binderDied() {
377 if (DEBUG) Log.d(TAG, "binderDeath");
378 handleDeath();
379 }
380}