Merge "Fix race condition in TileServices" into qt-dev
am: 5e23231f82
Change-Id: I8175c8a2cdafc9ee5edc1ea5eb568ab1a4f0618f
This commit is contained in:
@@ -32,6 +32,7 @@ import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.View.OnAttachStateChangeListener;
|
||||
import android.view.WindowManager;
|
||||
@@ -79,6 +80,9 @@ import com.android.internal.R;
|
||||
*/
|
||||
public class TileService extends Service {
|
||||
|
||||
private static final String TAG = "TileService";
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
/**
|
||||
* An activity that provides a user interface for adjusting TileService
|
||||
* preferences. Optional but recommended for apps that implement a
|
||||
@@ -381,18 +385,26 @@ public class TileService extends Service {
|
||||
private static final int MSG_TILE_CLICKED = 5;
|
||||
private static final int MSG_UNLOCK_COMPLETE = 6;
|
||||
private static final int MSG_START_SUCCESS = 7;
|
||||
private final String mTileServiceName;
|
||||
|
||||
public H(Looper looper) {
|
||||
super(looper);
|
||||
mTileServiceName = TileService.this.getClass().getSimpleName();
|
||||
}
|
||||
|
||||
private void logMessage(String message) {
|
||||
Log.d(TAG, mTileServiceName + " Handler - " + message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_TILE_ADDED:
|
||||
if (DEBUG) logMessage("MSG_TILE_ADDED");
|
||||
TileService.this.onTileAdded();
|
||||
break;
|
||||
case MSG_TILE_REMOVED:
|
||||
if (DEBUG) logMessage("MSG_TILE_REMOVED");
|
||||
if (mListening) {
|
||||
mListening = false;
|
||||
TileService.this.onStopListening();
|
||||
@@ -400,27 +412,32 @@ public class TileService extends Service {
|
||||
TileService.this.onTileRemoved();
|
||||
break;
|
||||
case MSG_STOP_LISTENING:
|
||||
if (DEBUG) logMessage("MSG_STOP_LISTENING");
|
||||
if (mListening) {
|
||||
mListening = false;
|
||||
TileService.this.onStopListening();
|
||||
}
|
||||
break;
|
||||
case MSG_START_LISTENING:
|
||||
if (DEBUG) logMessage("MSG_START_LISTENING");
|
||||
if (!mListening) {
|
||||
mListening = true;
|
||||
TileService.this.onStartListening();
|
||||
}
|
||||
break;
|
||||
case MSG_TILE_CLICKED:
|
||||
if (DEBUG) logMessage("MSG_TILE_CLICKED");
|
||||
mToken = (IBinder) msg.obj;
|
||||
TileService.this.onClick();
|
||||
break;
|
||||
case MSG_UNLOCK_COMPLETE:
|
||||
if (DEBUG) logMessage("MSG_UNLOCK_COMPLETE");
|
||||
if (mUnlockRunnable != null) {
|
||||
mUnlockRunnable.run();
|
||||
}
|
||||
break;
|
||||
case MSG_START_SUCCESS:
|
||||
if (DEBUG) logMessage("MSG_START_SUCCESS");
|
||||
try {
|
||||
mService.onStartSuccessful(mTileToken);
|
||||
} catch (RemoteException e) {
|
||||
|
||||
@@ -69,6 +69,7 @@ public class TileServiceManager {
|
||||
// Whether we have a pending bind going out to the service without a response yet.
|
||||
// This defaults to true to ensure tiles start out unavailable.
|
||||
private boolean mPendingBind = true;
|
||||
private boolean mStarted = false;
|
||||
|
||||
TileServiceManager(TileServices tileServices, Handler handler, ComponentName component,
|
||||
Tile tile) {
|
||||
@@ -90,7 +91,23 @@ public class TileServiceManager {
|
||||
Context context = mServices.getContext();
|
||||
context.registerReceiverAsUser(mUninstallReceiver,
|
||||
new UserHandle(ActivityManager.getCurrentUser()), filter, null, mHandler);
|
||||
ComponentName component = tileLifecycleManager.getComponent();
|
||||
}
|
||||
|
||||
boolean isLifecycleStarted() {
|
||||
return mStarted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the TileLifecycleManager by adding the corresponding component as a Tile and
|
||||
* binding to it if needed.
|
||||
*
|
||||
* This method should be called after constructing a TileServiceManager to guarantee that the
|
||||
* TileLifecycleManager has added the tile and bound to it at least once.
|
||||
*/
|
||||
void startLifecycleManagerAndAddTile() {
|
||||
mStarted = true;
|
||||
ComponentName component = mStateManager.getComponent();
|
||||
Context context = mServices.getContext();
|
||||
if (!TileLifecycleManager.isTileAdded(context, component)) {
|
||||
TileLifecycleManager.setTileAdded(context, component, true);
|
||||
mStateManager.onTileAdded();
|
||||
|
||||
@@ -51,6 +51,7 @@ import java.util.Comparator;
|
||||
public class TileServices extends IQSService.Stub {
|
||||
static final int DEFAULT_MAX_BOUND = 3;
|
||||
static final int REDUCED_MAX_BOUND = 1;
|
||||
private static final String TAG = "TileServices";
|
||||
|
||||
private final ArrayMap<CustomTile, TileServiceManager> mServices = new ArrayMap<>();
|
||||
private final ArrayMap<ComponentName, CustomTile> mTiles = new ArrayMap<>();
|
||||
@@ -87,6 +88,8 @@ public class TileServices extends IQSService.Stub {
|
||||
mTiles.put(component, tile);
|
||||
mTokenMap.put(service.getToken(), tile);
|
||||
}
|
||||
// Makes sure binding only happens after the maps have been populated
|
||||
service.startLifecycleManagerAndAddTile();
|
||||
return service;
|
||||
}
|
||||
|
||||
@@ -179,6 +182,11 @@ public class TileServices extends IQSService.Stub {
|
||||
verifyCaller(customTile);
|
||||
synchronized (mServices) {
|
||||
final TileServiceManager tileServiceManager = mServices.get(customTile);
|
||||
if (tileServiceManager == null || !tileServiceManager.isLifecycleStarted()) {
|
||||
Log.e(TAG, "TileServiceManager not started for " + customTile.getComponent(),
|
||||
new IllegalStateException());
|
||||
return;
|
||||
}
|
||||
tileServiceManager.clearPendingBind();
|
||||
tileServiceManager.setLastUpdate(System.currentTimeMillis());
|
||||
}
|
||||
@@ -194,6 +202,13 @@ public class TileServices extends IQSService.Stub {
|
||||
verifyCaller(customTile);
|
||||
synchronized (mServices) {
|
||||
final TileServiceManager tileServiceManager = mServices.get(customTile);
|
||||
// This should not happen as the TileServiceManager should have been started for the
|
||||
// first bind to happen.
|
||||
if (tileServiceManager == null || !tileServiceManager.isLifecycleStarted()) {
|
||||
Log.e(TAG, "TileServiceManager not started for " + customTile.getComponent(),
|
||||
new IllegalStateException());
|
||||
return;
|
||||
}
|
||||
tileServiceManager.clearPendingBind();
|
||||
}
|
||||
customTile.refreshState();
|
||||
|
||||
@@ -19,6 +19,7 @@ import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.os.Handler;
|
||||
@@ -88,7 +89,7 @@ public class TileServicesTest extends SysuiTestCase {
|
||||
assertEquals(NUM_FAKES, mManagers.size());
|
||||
|
||||
for (int i = 0; i < NUM_FAKES; i++) {
|
||||
Mockito.when(mManagers.get(i).getBindPriority()).thenReturn(i);
|
||||
when(mManagers.get(i).getBindPriority()).thenReturn(i);
|
||||
}
|
||||
mTileService.recalculateBindAllowance();
|
||||
for (int i = 0; i < NUM_FAKES; i++) {
|
||||
@@ -145,6 +146,7 @@ public class TileServicesTest extends SysuiTestCase {
|
||||
protected TileServiceManager onCreateTileService(ComponentName component, Tile qsTile) {
|
||||
TileServiceManager manager = mock(TileServiceManager.class);
|
||||
mManagers.add(manager);
|
||||
when(manager.isLifecycleStarted()).thenReturn(true);
|
||||
return manager;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user